diff --git a/docker/Dockerfile-yotta b/docker/Dockerfile-yotta new file mode 100644 index 00000000000..ef9c94d6854 --- /dev/null +++ b/docker/Dockerfile-yotta @@ -0,0 +1,63 @@ +# Adapted from yotta-docker (https://github.com/ARMmbed/yotta-docker/blob/master/Dockerfile) +# See ThirdPartyNotices for full license + +# ------------------------------------------------------------------------------ +# Pull base image +FROM ubuntu:bionic +MAINTAINER Richard Knoll + +# ------------------------------------------------------------------------------ +# Install base +RUN apt-get -y update && \ + apt-get -y dist-upgrade && \ + apt-get -y install curl git wget + +# ------------------------------------------------------------------------------ +# Install build tools +RUN apt-get -y install build-essential cmake ninja-build clang-3.9 + +# ------------------------------------------------------------------------------ +# Install arm gcc + +# For GNU 5: Add i386 architecture (later versions don't need this) +RUN dpkg --add-architecture i386 && \ + apt-get update && \ + apt-get -y upgrade && \ + apt-get -y dist-upgrade && \ + apt-get install -y libc6:i386 + +# ARM GNU Toolchain 5_4-2016q3 +RUN wget -O toolchain.tar.bz2 "https://developer.arm.com/-/media/Files/downloads/gnu-rm/5_4-2016q3/gcc-arm-none-eabi-5_4-2016q3-20160926-linux.tar.bz2?rev=111dee36f88b46728ac648cf41b4d375&revision=111dee36-f88b-4672-8ac6-48cf41b4d375" && \ + echo f7004b904541c09a8a0a7a52883c9e5b toolchain.tar.bz2 > /tmp/toolchain.tar.bz2.md5 && md5sum -c /tmp/toolchain.tar.bz2.md5 && rm /tmp/toolchain.tar.bz2.md5 && \ + tar -xf toolchain.tar.bz2 -C /opt && \ + rm toolchain.tar.bz2 +ENV PATH=/opt/gcc-arm-none-eabi-5_4-2016q3/bin:$PATH + +# ------------------------------------------------------------------------------ +# Install python +RUN apt-get -y install python python-setuptools python-usb python-pip + +# ------------------------------------------------------------------------------ +# Install python build requirements +RUN apt-get -y install python-dev libffi-dev libssl-dev libxml2-dev + +# ------------------------------------------------------------------------------ +# Install yotta +RUN pip install -U pyopenssl ndg-httpsclient pyasn1 requests pyusb==1.0.0 yotta==0.20.5 + +# ------------------------------------------------------------------------------ +# Install srecord +RUN apt-get -y install srecord + + +RUN apt-get install rlwrap + +RUN curl https://deb.nodesource.com/node_4.x/pool/main/n/nodejs/nodejs_4.4.3-1nodesource1~trusty1_amd64.deb > node.deb \ + && dpkg -i node.deb \ + && rm node.deb + +RUN curl https://cmake.org/files/v3.8/cmake-3.8.2-Linux-x86_64.tar.gz | (cd /usr; tar zxf - --strip-components=1) + +RUN useradd -m build + +COPY go.js /home/build diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000000..64284617d4d --- /dev/null +++ b/docker/README.md @@ -0,0 +1,4 @@ +This docker script is used to build the docker image for the project. The docker image is used to run the project in a containerized environment. The docker image is built using the Dockerfile. The docker image is built using the following command: +``` +docker build -t pxt_microbit_build -f Dockerfile-yotta . +``` \ No newline at end of file diff --git a/libs/arcadeshield/_locales/arcadeshield-jsdoc-strings.json b/libs/arcadeshield/_locales/arcadeshield-jsdoc-strings.json new file mode 100644 index 00000000000..d07a9a45f07 --- /dev/null +++ b/libs/arcadeshield/_locales/arcadeshield-jsdoc-strings.json @@ -0,0 +1,47 @@ +{ + "SImage.blit": "Copy an image from a source rectangle to a destination rectangle, stretching or\ncompressing to fit the dimensions of the destination rectangle, if necessary.", + "SImage.blitRow": "Scale and copy a row of pixels from a texture.", + "SImage.clone": "Return a copy of the current image", + "SImage.copyFrom": "Sets all pixels in the current image from the other image, which has to be of the same size and\nbpp.", + "SImage.doubled": "Stretches the image in both directions by 100%", + "SImage.doubledX": "Stretches the image horizontally by 100%", + "SImage.doubledY": "Stretches the image vertically by 100%", + "SImage.drawCircle": "Draw a circle", + "SImage.drawIcon": "Draw an icon (monochromatic image) using given color", + "SImage.drawImage": "Draw given image on the current image", + "SImage.drawLine": "Draw a line", + "SImage.drawRect": "Draw an empty rectangle", + "SImage.drawTransparentImage": "Draw given image with transparent background on the current image", + "SImage.equals": "Returns true if the provided image is the same as this image,\notherwise returns false.", + "SImage.fill": "Fill entire image with a given color", + "SImage.fillCircle": "Fills a circle", + "SImage.fillPolygon4": "Fills a 4-side-polygon", + "SImage.fillRect": "Fill a rectangle", + "SImage.fillTriangle": "Fills a triangle", + "SImage.flipX": "Flips (mirrors) pixels horizontally in the current image", + "SImage.flipY": "Flips (mirrors) pixels vertically in the current image", + "SImage.getPixel": "Get a pixel color", + "SImage.getRows": "Copy row(s) of pixel from image to buffer (8 bit per pixel).", + "SImage.height": "Get the height of the image", + "SImage.isMono": "True if the image is monochromatic (black and white)", + "SImage.mapRect": "Replace colors in a rectangle", + "SImage.overlapsWith": "Check if the current image \"collides\" with another", + "SImage.replace": "Replaces one color in an image with another", + "SImage.rotated": "Returns an image rotated by -90, 0, 90, 180, 270 deg clockwise", + "SImage.scroll": "Every pixel in image is moved by (dx,dy)", + "SImage.setPixel": "Set pixel color", + "SImage.setRows": "Copy row(s) of pixel from buffer to image.", + "SImage.transposed": "Returns a transposed image (with X/Y swapped)", + "SImage.width": "Get the width of the image", + "ScreenImage.brightness": "Gets current screen backlight brightness (0-100)", + "ScreenImage.setBrightness": "Sets the screen backlight brightness (10-100)", + "helpers.imageRotated": "Returns an image rotated by 90, 180, 270 deg clockwise", + "images": "Creation, manipulation and display of LED images.\n\nImage manipulation blocks", + "images._image": "An image", + "images._image|param|image": "the image", + "img": "Tagged image literal converter", + "simage.create": "Create new empty (transparent) image", + "simage.doubledIcon": "Double the size of an icon", + "simage.ofBuffer": "Create new image with given content", + "simage.screenImage": "Get the screen image" +} \ No newline at end of file diff --git a/libs/arcadeshield/_locales/arcadeshield-strings.json b/libs/arcadeshield/_locales/arcadeshield-strings.json new file mode 100644 index 00000000000..bec08174fb9 --- /dev/null +++ b/libs/arcadeshield/_locales/arcadeshield-strings.json @@ -0,0 +1,40 @@ +{ + "SImage.clone|block": "clone %picture=variables_get", + "SImage.drawLine|block": "draw line in %picture=variables_get from x %x0 y %y0 to x %x1 y %y1 %c=colorindexpicker", + "SImage.drawRect|block": "draw rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker", + "SImage.equals|block": "$this is equal to image $other", + "SImage.fillRect|block": "fill rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker", + "SImage.fill|block": "fill %picture=variables_get with %c=colorindexpicker", + "SImage.flipX|block": "flip %picture=variables_get horizontally", + "SImage.flipY|block": "flip %picture=variables_get vertically", + "SImage.getPixel|block": "%picture=variables_get color at x %x y %y", + "SImage.replace|block": "change color in %picture=variables_get from %from=colorindexpicker to %to=colorindexpicker", + "SImage.setPixel|block": "set %picture=variables_get color at x %x y %y to %c=colorindexpicker", + "helpers|block": "helpers", + "images._dialogImage|block": "%img", + "images._image|block": "$image", + "images._screenImage|block": "%img", + "images._spriteImage|block": "%img", + "images._tileImage|block": "%img", + "images._tileMapImage|block": "%img", + "images._tile|block": "%tile", + "images|block": "images", + "simage.create|block": "create image width %width height %height", + "simage.screenImage|block": "screen", + "simage|block": "simage", + "{id:category}Control": "Control", + "{id:category}Helpers": "Helpers", + "{id:category}Images": "Images", + "{id:category}SImage": "SImage", + "{id:category}Scene": "Scene", + "{id:category}ScreenImage": "ScreenImage", + "{id:category}Simage": "Simage", + "{id:category}Texteffects": "Texteffects", + "{id:category}_helpers_workaround": "_helpers_workaround", + "{id:category}_screen_internal": "_screen_internal", + "{id:group}Compare": "Compare", + "{id:group}Create": "Create", + "{id:group}Drawing": "Drawing", + "{id:group}Tiles": "Tiles", + "{id:group}Transformations": "Transformations" +} \ No newline at end of file diff --git a/libs/arcadeshield/_locales/screen---st7735-jsdoc-strings.json b/libs/arcadeshield/_locales/screen---st7735-jsdoc-strings.json new file mode 100644 index 00000000000..634ab7e1505 --- /dev/null +++ b/libs/arcadeshield/_locales/screen---st7735-jsdoc-strings.json @@ -0,0 +1,47 @@ +{ + "SImage.blit": "Copy an image from a source rectangle to a destination rectangle, stretching or\ncompressing to fit the dimensions of the destination rectangle, if necessary.", + "SImage.blitRow": "Scale and copy a row of pixels from a texture.", + "SImage.clone": "Return a copy of the current image\n\nReturn a copy of the current image", + "SImage.copyFrom": "Sets all pixels in the current image from the other image, which has to be of the same size and\nbpp.", + "SImage.doubled": "Stretches the image in both directions by 100%", + "SImage.doubledX": "Stretches the image horizontally by 100%", + "SImage.doubledY": "Stretches the image vertically by 100%", + "SImage.drawCircle": "Draw a circle", + "SImage.drawIcon": "Draw an icon (monochromatic image) using given color", + "SImage.drawImage": "Draw given image on the current image", + "SImage.drawLine": "Draw a line", + "SImage.drawRect": "Draw an empty rectangle", + "SImage.drawTransparentImage": "Draw given image with transparent background on the current image", + "SImage.equals": "Returns true if the provided image is the same as this image,\notherwise returns false.", + "SImage.fill": "Fill entire image with a given color\n\nFill entire image with a given color", + "SImage.fillCircle": "Fills a circle", + "SImage.fillPolygon4": "Fills a 4-side-polygon", + "SImage.fillRect": "Fill a rectangle", + "SImage.fillTriangle": "Fills a triangle", + "SImage.flipX": "Flips (mirrors) pixels horizontally in the current image\n\nFlips (mirrors) pixels horizontally in the current image", + "SImage.flipY": "Flips (mirrors) pixels vertically in the current image\n\nFlips (mirrors) pixels vertically in the current image", + "SImage.getPixel": "Get a pixel color\n\nGet a pixel color", + "SImage.getRows": "Copy row(s) of pixel from image to buffer (8 bit per pixel).", + "SImage.height": "Get the height of the image", + "SImage.isMono": "True if the image is monochromatic (black and white)", + "SImage.mapRect": "Replace colors in a rectangle", + "SImage.overlapsWith": "Check if the current image \"collides\" with another", + "SImage.replace": "Replaces one color in an image with another\n\nReplaces one color in an image with another", + "SImage.rotated": "Returns an image rotated by -90, 0, 90, 180, 270 deg clockwise", + "SImage.scroll": "Every pixel in image is moved by (dx,dy)\n\nEvery pixel in image is moved by (dx,dy)", + "SImage.setPixel": "Set pixel color\n\nSet pixel color", + "SImage.setRows": "Copy row(s) of pixel from buffer to image.", + "SImage.transposed": "Returns a transposed image (with X/Y swapped)", + "SImage.width": "Get the width of the image", + "ScreenImage.brightness": "Gets current screen backlight brightness (0-100)", + "ScreenImage.setBrightness": "Sets the screen backlight brightness (10-100)", + "helpers.imageRotated": "Returns an image rotated by 90, 180, 270 deg clockwise", + "image.create": "Create new empty (transparent) image", + "image.doubledIcon": "Double the size of an icon", + "image.ofBuffer": "Create new image with given content", + "images": "Creation, manipulation and display of LED images.\n\nImage manipulation blocks", + "images._image": "An image", + "images._image|param|image": "the image", + "img": "Tagged image literal converter", + "simage.screenImage": "Get the screen image" +} \ No newline at end of file diff --git a/libs/arcadeshield/_locales/screen---st7735-strings.json b/libs/arcadeshield/_locales/screen---st7735-strings.json new file mode 100644 index 00000000000..fab0a1e7002 --- /dev/null +++ b/libs/arcadeshield/_locales/screen---st7735-strings.json @@ -0,0 +1,42 @@ +{ + "SImage.clone|block": "clone %picture=variables_get", + "SImage.drawLine|block": "draw line in %picture=variables_get from x %x0 y %y0 to x %x1 y %y1 %c=colorindexpicker", + "SImage.drawRect|block": "draw rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker", + "SImage.equals|block": "$this is equal to image $other", + "SImage.fillRect|block": "fill rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker", + "SImage.fill|block": "fill %picture=variables_get with %c=colorindexpicker", + "SImage.flipX|block": "flip %picture=variables_get horizontally", + "SImage.flipY|block": "flip %picture=variables_get vertically", + "SImage.getPixel|block": "%picture=variables_get color at x %x y %y", + "SImage.replace|block": "change color in %picture=variables_get from %from=colorindexpicker to %to=colorindexpicker", + "SImage.setPixel|block": "set %picture=variables_get color at x %x y %y to %c=colorindexpicker", + "helpers|block": "helpers", + "images._dialogImage|block": "%img", + "images._image|block": "$image", + "images._screenImage|block": "%img", + "images._spriteImage|block": "%img", + "images._tileImage|block": "%img", + "images._tileMapImage|block": "%img", + "images._tile|block": "%tile", + "images|block": "images", + "image|block": "image", + "simage.create|block": "create image width %width height %height", + "simage.screenImage|block": "screen", + "simage|block": "simage", + "{id:category}Control": "Control", + "{id:category}Helpers": "Helpers", + "{id:category}Image": "Image", + "{id:category}Images": "Images", + "{id:category}SImage": "SImage", + "{id:category}Scene": "Scene", + "{id:category}ScreenImage": "ScreenImage", + "{id:category}Simage": "Simage", + "{id:category}Texteffects": "Texteffects", + "{id:category}_helpers_workaround": "_helpers_workaround", + "{id:category}_screen_internal": "_screen_internal", + "{id:group}Compare": "Compare", + "{id:group}Create": "Create", + "{id:group}Drawing": "Drawing", + "{id:group}Tiles": "Tiles", + "{id:group}Transformations": "Transformations" +} \ No newline at end of file diff --git a/libs/arcadeshield/_locales/st7735-jsdoc-strings.json b/libs/arcadeshield/_locales/st7735-jsdoc-strings.json new file mode 100644 index 00000000000..d07a9a45f07 --- /dev/null +++ b/libs/arcadeshield/_locales/st7735-jsdoc-strings.json @@ -0,0 +1,47 @@ +{ + "SImage.blit": "Copy an image from a source rectangle to a destination rectangle, stretching or\ncompressing to fit the dimensions of the destination rectangle, if necessary.", + "SImage.blitRow": "Scale and copy a row of pixels from a texture.", + "SImage.clone": "Return a copy of the current image", + "SImage.copyFrom": "Sets all pixels in the current image from the other image, which has to be of the same size and\nbpp.", + "SImage.doubled": "Stretches the image in both directions by 100%", + "SImage.doubledX": "Stretches the image horizontally by 100%", + "SImage.doubledY": "Stretches the image vertically by 100%", + "SImage.drawCircle": "Draw a circle", + "SImage.drawIcon": "Draw an icon (monochromatic image) using given color", + "SImage.drawImage": "Draw given image on the current image", + "SImage.drawLine": "Draw a line", + "SImage.drawRect": "Draw an empty rectangle", + "SImage.drawTransparentImage": "Draw given image with transparent background on the current image", + "SImage.equals": "Returns true if the provided image is the same as this image,\notherwise returns false.", + "SImage.fill": "Fill entire image with a given color", + "SImage.fillCircle": "Fills a circle", + "SImage.fillPolygon4": "Fills a 4-side-polygon", + "SImage.fillRect": "Fill a rectangle", + "SImage.fillTriangle": "Fills a triangle", + "SImage.flipX": "Flips (mirrors) pixels horizontally in the current image", + "SImage.flipY": "Flips (mirrors) pixels vertically in the current image", + "SImage.getPixel": "Get a pixel color", + "SImage.getRows": "Copy row(s) of pixel from image to buffer (8 bit per pixel).", + "SImage.height": "Get the height of the image", + "SImage.isMono": "True if the image is monochromatic (black and white)", + "SImage.mapRect": "Replace colors in a rectangle", + "SImage.overlapsWith": "Check if the current image \"collides\" with another", + "SImage.replace": "Replaces one color in an image with another", + "SImage.rotated": "Returns an image rotated by -90, 0, 90, 180, 270 deg clockwise", + "SImage.scroll": "Every pixel in image is moved by (dx,dy)", + "SImage.setPixel": "Set pixel color", + "SImage.setRows": "Copy row(s) of pixel from buffer to image.", + "SImage.transposed": "Returns a transposed image (with X/Y swapped)", + "SImage.width": "Get the width of the image", + "ScreenImage.brightness": "Gets current screen backlight brightness (0-100)", + "ScreenImage.setBrightness": "Sets the screen backlight brightness (10-100)", + "helpers.imageRotated": "Returns an image rotated by 90, 180, 270 deg clockwise", + "images": "Creation, manipulation and display of LED images.\n\nImage manipulation blocks", + "images._image": "An image", + "images._image|param|image": "the image", + "img": "Tagged image literal converter", + "simage.create": "Create new empty (transparent) image", + "simage.doubledIcon": "Double the size of an icon", + "simage.ofBuffer": "Create new image with given content", + "simage.screenImage": "Get the screen image" +} \ No newline at end of file diff --git a/libs/arcadeshield/_locales/st7735-strings.json b/libs/arcadeshield/_locales/st7735-strings.json new file mode 100644 index 00000000000..bec08174fb9 --- /dev/null +++ b/libs/arcadeshield/_locales/st7735-strings.json @@ -0,0 +1,40 @@ +{ + "SImage.clone|block": "clone %picture=variables_get", + "SImage.drawLine|block": "draw line in %picture=variables_get from x %x0 y %y0 to x %x1 y %y1 %c=colorindexpicker", + "SImage.drawRect|block": "draw rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker", + "SImage.equals|block": "$this is equal to image $other", + "SImage.fillRect|block": "fill rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker", + "SImage.fill|block": "fill %picture=variables_get with %c=colorindexpicker", + "SImage.flipX|block": "flip %picture=variables_get horizontally", + "SImage.flipY|block": "flip %picture=variables_get vertically", + "SImage.getPixel|block": "%picture=variables_get color at x %x y %y", + "SImage.replace|block": "change color in %picture=variables_get from %from=colorindexpicker to %to=colorindexpicker", + "SImage.setPixel|block": "set %picture=variables_get color at x %x y %y to %c=colorindexpicker", + "helpers|block": "helpers", + "images._dialogImage|block": "%img", + "images._image|block": "$image", + "images._screenImage|block": "%img", + "images._spriteImage|block": "%img", + "images._tileImage|block": "%img", + "images._tileMapImage|block": "%img", + "images._tile|block": "%tile", + "images|block": "images", + "simage.create|block": "create image width %width height %height", + "simage.screenImage|block": "screen", + "simage|block": "simage", + "{id:category}Control": "Control", + "{id:category}Helpers": "Helpers", + "{id:category}Images": "Images", + "{id:category}SImage": "SImage", + "{id:category}Scene": "Scene", + "{id:category}ScreenImage": "ScreenImage", + "{id:category}Simage": "Simage", + "{id:category}Texteffects": "Texteffects", + "{id:category}_helpers_workaround": "_helpers_workaround", + "{id:category}_screen_internal": "_screen_internal", + "{id:group}Compare": "Compare", + "{id:group}Create": "Create", + "{id:group}Drawing": "Drawing", + "{id:group}Tiles": "Tiles", + "{id:group}Transformations": "Transformations" +} \ No newline at end of file diff --git a/libs/arcadeshield/arcadegamepad.h b/libs/arcadeshield/arcadegamepad.h new file mode 100644 index 00000000000..bcfe4577511 --- /dev/null +++ b/libs/arcadeshield/arcadegamepad.h @@ -0,0 +1,49 @@ +// Autogenerated C header file for Arcade Gamepad +#ifndef _JACDAC_SPEC_ARCADE_GAMEPAD_H +#define _JACDAC_SPEC_ARCADE_GAMEPAD_H 1 + +#define JD_SERVICE_CLASS_ARCADE_GAMEPAD 0x1deaa06e + +// enum Button (uint8_t) +#define JD_ARCADE_GAMEPAD_BUTTON_LEFT 0x1 +#define JD_ARCADE_GAMEPAD_BUTTON_UP 0x2 +#define JD_ARCADE_GAMEPAD_BUTTON_RIGHT 0x3 +#define JD_ARCADE_GAMEPAD_BUTTON_DOWN 0x4 +#define JD_ARCADE_GAMEPAD_BUTTON_A 0x5 +#define JD_ARCADE_GAMEPAD_BUTTON_B 0x6 +#define JD_ARCADE_GAMEPAD_BUTTON_MENU 0x7 +#define JD_ARCADE_GAMEPAD_BUTTON_SELECT 0x8 +#define JD_ARCADE_GAMEPAD_BUTTON_RESET 0x9 +#define JD_ARCADE_GAMEPAD_BUTTON_EXIT 0xa + +/** + * Indicates which buttons are currently active (pressed). + * `pressure` should be `0xff` for digital buttons, and proportional for analog ones. + */ +#define JD_ARCADE_GAMEPAD_REG_BUTTONS JD_REG_READING +typedef struct jd_arcade_gamepad_buttons { + uint8_t button; // Button + uint8_t pressure; // ratio u0.8 +} jd_arcade_gamepad_buttons_t; + + +/** + * Constant. Indicates number of players supported and which buttons are present on the controller. + */ +#define JD_ARCADE_GAMEPAD_REG_AVAILABLE_BUTTONS 0x180 +typedef struct jd_arcade_gamepad_available_buttons { + uint8_t button[0]; // Button +} jd_arcade_gamepad_available_buttons_t; + + +/** + * Argument: button Button (uint8_t). Emitted when button goes from inactive to active. + */ +#define JD_ARCADE_GAMEPAD_EV_DOWN JD_EV_ACTIVE + +/** + * Argument: button Button (uint8_t). Emitted when button goes from active to inactive. + */ +#define JD_ARCADE_GAMEPAD_EV_UP JD_EV_INACTIVE + +#endif diff --git a/libs/arcadeshield/arcadesound.h b/libs/arcadeshield/arcadesound.h new file mode 100644 index 00000000000..7b47d360c9d --- /dev/null +++ b/libs/arcadeshield/arcadesound.h @@ -0,0 +1,31 @@ +// Autogenerated C header file for Arcade sound +#ifndef _JACDAC_SPEC_ARCADE_SOUND_H +#define _JACDAC_SPEC_ARCADE_SOUND_H 1 + +#define JD_SERVICE_CLASS_ARCADE_SOUND 0x1fc63606 + +/** + * Argument: samples bytes. Play samples, which are single channel, signed 16-bit little endian values. + */ +#define JD_ARCADE_SOUND_CMD_PLAY 0x80 + +/** + * Read-write Hz u22.10 (uint32_t). Get or set playback sample rate (in samples per second). + * If you set it, read it back, as the value may be rounded up or down. + */ +#define JD_ARCADE_SOUND_REG_SAMPLE_RATE 0x80 + +/** + * Constant B uint32_t. The size of the internal audio buffer. + */ +#define JD_ARCADE_SOUND_REG_BUFFER_SIZE 0x180 + +/** + * Read-only B uint32_t. How much data is still left in the buffer to play. + * Clients should not send more data than `buffer_size - buffer_pending`, + * but can keep the `buffer_pending` as low as they want to ensure low latency + * of audio playback. + */ +#define JD_ARCADE_SOUND_REG_BUFFER_PENDING 0x181 + +#endif diff --git a/libs/arcadeshield/config.ts b/libs/arcadeshield/config.ts new file mode 100644 index 00000000000..f3a97a501a6 --- /dev/null +++ b/libs/arcadeshield/config.ts @@ -0,0 +1,36 @@ +// there's no UF2 bootloader for 52833 yet, so we specify example configuration here +namespace config { + export const PIN_BTNMX_LATCH = DAL.P0_9 + export const PIN_BTNMX_CLOCK = DAL.P1_0 + export const PIN_BTNMX_DATA = DAL.P0_1 + + // pybadge-like layout + export const PIN_BTN_LEFT = 1050 + export const PIN_BTN_UP = 1051 + export const PIN_BTN_DOWN = 1052 + export const PIN_BTN_RIGHT = 1053 + export const PIN_BTN_A = 1054 + export const PIN_BTN_B = 1055 + export const PIN_BTN_MENU = 1056 + + export const PIN_JACK_SND = DAL.P0_0 + + export const PIN_DISPLAY_SCK = DAL.P0_17 + export const PIN_DISPLAY_MOSI = DAL.P0_13 + export const PIN_DISPLAY_MISO = DAL.P0_1 + export const PIN_DISPLAY_BL = DAL.P0_26 + export const PIN_DISPLAY_DC = DAL.P0_10 + export const PIN_DISPLAY_RST = DAL.P1_2 + + // Jacdac, when jacdaptor is connected, is on the accessibility pin (P12) + export const PIN_JACK_TX = DAL.P0_12 + + export const DISPLAY_WIDTH = 160 + export const DISPLAY_HEIGHT = 128 + + export const DISPLAY_TYPE = 4242 // smart display + + export const DISPLAY_CFG0 = 0x00000080 + export const DISPLAY_CFG1 = 0x00000603 + export const DISPLAY_CFG2 = 8 +} diff --git a/libs/arcadeshield/config_nrf.h b/libs/arcadeshield/config_nrf.h new file mode 100644 index 00000000000..c996453a5d6 --- /dev/null +++ b/libs/arcadeshield/config_nrf.h @@ -0,0 +1,59 @@ +#include "NRF52Pin.h" +#include "NRF52SPI.h" + +#define CODAL_PIN NRF52Pin +#define CODAL_SPI NRF52SPI + +#define MY_DISPLAY_WIDTH 160 +#define MY_DISPLAY_HEIGHT 128 +#define MY_DISPLAY_TYPE 4242 // smart display +#define MY_DISPLAY_CFG0 0x00000080 +#define MY_DISPLAY_CFG1 0x00000603 +#define MY_DISPLAY_CFG2 32 + +#define MY_PIN_BTNMX_LATCH &uBit.io.P9 // 9 // DAL.P0_9 EC P9 +#define MY_PIN_BTNMX_CLOCK &uBit.io.P20 // 32 // DAL.P1_0 EC P20 +#define MY_PIN_BTNMX_DATA &uBit.io.P14 // 1 // DAL.P0_1 EC P14 + +#define MY_PIN_DISPLAY_SCK &uBit.io.P13 // 17 // DAL.P0_17 EC P13 +#define MY_PIN_DISPLAY_MOSI &uBit.io.P15 // 13 // DAL.P0_13 EC P15 +#define MY_PIN_DISPLAY_MISO &uBit.io.P14 // 1 // DAL.P0_1 EC P14 +#define MY_PIN_DISPLAY_BL &uBit.io.P19 // 26 // DAL.P0_26 EC P19 +#define MY_PIN_DISPLAY_DC &uBit.io.P8 // 10 // DAL.P0_10 EC P8 +#define MY_PIN_DISPLAY_RST &uBit.io.P16 // DAL.P1_2 EC P16 +#define MY_PIN_DISPLAY_CS ((CODAL_PIN*)NULL) // 0xff // not connected +#define MY_PIN_LED ((CODAL_PIN*)NULL) // 0xff // not connected + +// #define CFG_PIN_NAME_MSK 0xffff +#undef DEV_NUM_PINS +#define DEV_NUM_PINS 48 +#define DEVICE_ID_IO_P0 100 + +// remove the indirection through configuration +#undef PIN +#undef LOOKUP_PIN +#define PIN(name) MY_PIN_##name +#define LOOKUP_PIN(name) PIN(name) // pxt::myLookupPin(PIN(name)) + +#define PXT_INTERNAL_KEY_UP 2050 +#define PXT_INTERNAL_KEY_DOWN 2051 +#define DEVICE_ID_FIRST_BUTTON 4000 + + +namespace pxt { + uint32_t readButtonMultiplexer(int bits); + void disableButtonMultiplexer(); +} + + +// // pybadge-like layout +// export const PIN_BTN_LEFT = 1050 +// export const PIN_BTN_UP = 1051 +// export const PIN_BTN_DOWN = 1052 +// export const PIN_BTN_RIGHT = 1053 +// export const PIN_BTN_A = 1054 +// export const PIN_BTN_B = 1055 +// export const PIN_BTN_MENU = 1056 +// export const PIN_JACK_SND = DAL.P0_0 +// // Jacdac, when jacdaptor is connected, is on the accessibility pin (P12) +// export const PIN_JACK_TX = DAL.P0_12 \ No newline at end of file diff --git a/libs/arcadeshield/indexedscreen.h b/libs/arcadeshield/indexedscreen.h new file mode 100644 index 00000000000..1cc0005a4b2 --- /dev/null +++ b/libs/arcadeshield/indexedscreen.h @@ -0,0 +1,85 @@ +// Autogenerated C header file for Indexed screen +#ifndef _JACDAC_SPEC_INDEXED_SCREEN_H +#define _JACDAC_SPEC_INDEXED_SCREEN_H 1 + +#define JD_SERVICE_CLASS_INDEXED_SCREEN 0x16fa36e5 + +/** + * Sets the update window for subsequent `set_pixels` commands. + */ +#define JD_INDEXED_SCREEN_CMD_START_UPDATE 0x81 +typedef struct jd_indexed_screen_start_update { + uint16_t x; // px + uint16_t y; // px + uint16_t width; // px + uint16_t height; // px +} jd_indexed_screen_start_update_t; + + +/** + * Argument: pixels bytes. Set pixels in current window, according to current palette. + * Each "line" of data is aligned to a byte. + */ +#define JD_INDEXED_SCREEN_CMD_SET_PIXELS 0x83 + +/** + * Read-write ratio u0.8 (uint8_t). Set backlight brightness. + * If set to `0` the display may go to sleep. + */ +#define JD_INDEXED_SCREEN_REG_BRIGHTNESS JD_REG_INTENSITY + +/** + * The current palette. + * The color entry repeats `1 << bits_per_pixel` times. + * This register may be write-only. + */ +#define JD_INDEXED_SCREEN_REG_PALETTE 0x80 +typedef struct jd_indexed_screen_palette { + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t padding; +} jd_indexed_screen_palette_t; + + +/** + * Constant bit uint8_t. Determines the number of palette entries. + * Typical values are 1, 2, 4, or 8. + */ +#define JD_INDEXED_SCREEN_REG_BITS_PER_PIXEL 0x180 + +/** + * Constant px uint16_t. Screen width in "natural" orientation. + */ +#define JD_INDEXED_SCREEN_REG_WIDTH 0x181 + +/** + * Constant px uint16_t. Screen height in "natural" orientation. + */ +#define JD_INDEXED_SCREEN_REG_HEIGHT 0x182 + +/** + * Read-write bool (uint8_t). If true, consecutive pixels in the "width" direction are sent next to each other (this is typical for graphics cards). + * If false, consecutive pixels in the "height" direction are sent next to each other. + * For embedded screen controllers, this is typically true iff `width < height` + * (in other words, it's only true for portrait orientation screens). + * Some controllers may allow the user to change this (though the refresh order may not be optimal then). + * This is independent of the `rotation` register. + */ +#define JD_INDEXED_SCREEN_REG_WIDTH_MAJOR 0x81 + +/** + * Read-write px uint8_t. Every pixel sent over wire is represented by `up_sampling x up_sampling` square of physical pixels. + * Some displays may allow changing this (which will also result in changes to `width` and `height`). + * Typical values are 1 and 2. + */ +#define JD_INDEXED_SCREEN_REG_UP_SAMPLING 0x82 + +/** + * Read-write ° uint16_t. Possible values are 0, 90, 180 and 270 only. + * Write to this register do not affect `width` and `height` registers, + * and may be ignored by some screens. + */ +#define JD_INDEXED_SCREEN_REG_ROTATION 0x83 + +#endif diff --git a/libs/arcadeshield/jddisplay.cpp b/libs/arcadeshield/jddisplay.cpp new file mode 100644 index 00000000000..4d444bbb834 --- /dev/null +++ b/libs/arcadeshield/jddisplay.cpp @@ -0,0 +1,359 @@ +#include "pxt.h" + +#include "jddisplay.h" + +#include "config_nrf.h" + +#define VLOG NOLOG +//#define VLOG DMESG + +namespace pxt { + +codal::CodalDevice device; + +#define ALIGN(x) (((x) + 3) & ~3) + +static void jd_panic(void) { + target_panic(121); // PANIC_SCREEN_ERROR +} + +static int jd_shift_frame(jd_frame_t *frame) { + int psize = frame->size; + jd_packet_t *pkt = (jd_packet_t *)frame; + int oldsz = pkt->service_size + 4; + if (ALIGN(oldsz) >= psize) + return 0; // nothing to shift + + int ptr; + if (frame->data[oldsz] == 0xff) { + ptr = frame->data[oldsz + 1]; + if (ptr >= psize) + return 0; // End-of-frame + if (ptr <= oldsz) { + DMESG("invalid super-frame %d %d", ptr, oldsz); + return 0; // don't let it go back, must be some corruption + } + } else { + ptr = ALIGN(oldsz); + } + + // assume the first one got the ACK sorted + frame->flags &= ~JD_FRAME_FLAG_ACK_REQUESTED; + + uint8_t *src = &frame->data[ptr]; + int newsz = *src + 4; + if (ptr + newsz > psize) { + DMESG("invalid super-frame %d %d %d", ptr, newsz, psize); + return 0; + } + uint32_t *dst = (uint32_t *)frame->data; + uint32_t *srcw = (uint32_t *)src; + // don't trust memmove() + for (int i = 0; i < newsz; i += 4) + *dst++ = *srcw++; + // store ptr + ptr += ALIGN(newsz); + frame->data[newsz] = 0xff; + frame->data[newsz + 1] = ptr; + + return 1; +} + +static void *jd_push_in_frame(jd_frame_t *frame, unsigned service_num, unsigned service_cmd, + unsigned service_size) { + if (service_num >> 8) + jd_panic(); + if (service_cmd >> 16) + jd_panic(); + uint8_t *dst = frame->data + frame->size; + unsigned szLeft = (uint8_t *)frame + sizeof(*frame) - dst; + if (service_size + 4 > szLeft) + return NULL; + *dst++ = service_size; + *dst++ = service_num; + *dst++ = service_cmd & 0xff; + *dst++ = service_cmd >> 8; + frame->size += ALIGN(service_size + 4); + return dst; +} + +JDDisplay::JDDisplay(SPI *spi, Pin *cs, Pin *flow) : spi(spi), cs(cs), flow(flow) { + inProgress = false; + stepWaiting = false; + displayServiceNum = 0; + controlsStartServiceNum = 0; + controlsEndServiceNum = 0; + soundServiceNum = 0; + buttonState = 0; + brightness = 100; + soundBufferPending = 0; + soundSampleRate = 44100; + avgFrameTime = 26300; // start with a reasonable default + lastFrameTimestamp = 0; + + EventModel::defaultEventBus->listen(DEVICE_ID_DISPLAY, 4243, this, &JDDisplay::sendDone); + + flow->getDigitalValue(PullMode::Down); + EventModel::defaultEventBus->listen(flow->id, DEVICE_PIN_EVENT_ON_EDGE, this, + &JDDisplay::onFlowHi, MESSAGE_BUS_LISTENER_IMMEDIATE); + flow->eventOn(DEVICE_PIN_EVT_RISE); +} + +void JDDisplay::waitForSendDone() { + if (inProgress) + fiber_wait_for_event(DEVICE_ID_DISPLAY, 4242); +} + +void JDDisplay::sendDone(Event) { + inProgress = false; + Event(DEVICE_ID_DISPLAY, 4242); +} + +void *JDDisplay::queuePkt(uint32_t service_num, uint32_t service_cmd, uint32_t size) { + void *res = jd_push_in_frame(&sendFrame, service_num, service_cmd, size); + if (res == NULL) + target_panic(122); // PANIC_SCREEN_ERROR + return res; +} + +void JDDisplay::flushSend() { + if (cs) + cs->setDigitalValue(0); + spi->startTransfer((uint8_t *)&sendFrame, sizeof(sendFrame), (uint8_t *)&recvFrame, + sizeof(recvFrame), &JDDisplay::stepStatic, this); +} + +void JDDisplay::stepStatic(void *p) { + ((JDDisplay *)p)->step(); +} + +// We assume EIC IRQ pre-empts SPI/DMA IRQ (that is the numerical priority value of EIC is lower) +// This is true for codal STM32, SAMD, and NRF52 +void JDDisplay::onFlowHi(Event) { + if (stepWaiting) + step(); +} + +void JDDisplay::handleIncoming(jd_packet_t *pkt) { + if (pkt->service_number == JD_SERVICE_NUMBER_CTRL && + pkt->service_command == JD_CMD_ADVERTISEMENT_DATA) { + uint32_t *servptr = (uint32_t *)pkt->data; + int numServ = pkt->service_size >> 2; + for (uint8_t servIdx = 1; servIdx < numServ; ++servIdx) { + uint32_t service_class = servptr[servIdx]; + if (service_class == JD_SERVICE_CLASS_INDEXED_SCREEN) { + displayServiceNum = servIdx; + VLOG("JDA: found screen, serv=%d", servIdx); + } else if (service_class == JD_SERVICE_CLASS_ARCADE_GAMEPAD) { + if (!controlsStartServiceNum) + controlsStartServiceNum = servIdx; + controlsEndServiceNum = servIdx; + VLOG("JDA: found controls, serv=%d", servIdx); + } else if (service_class == JD_SERVICE_CLASS_ARCADE_SOUND) { + soundServiceNum = servIdx; + VLOG("JDA: found sound, serv=%d", servIdx); + } else { + VLOG("JDA: unknown service: %x", service_class); + } + } + } else if (pkt->service_number == JD_SERVICE_NUMBER_CTRL && + pkt->service_command == JD_CMD_CTRL_NOOP) { + // do nothing + } else if (pkt->service_number == soundServiceNum) { + switch (pkt->service_command) { + case JD_GET(JD_ARCADE_SOUND_REG_BUFFER_PENDING): + soundBufferPending = *(uint32_t *)pkt->data; + break; + case JD_GET(JD_ARCADE_SOUND_REG_SAMPLE_RATE): + soundSampleRate = *(uint32_t *)pkt->data >> 10; + break; + } + } else if (pkt->service_number == displayServiceNum) { + switch (pkt->service_command) { + case JD_GET(JD_INDEXED_SCREEN_REG_HEIGHT): + screenHeight = *(uint16_t *)pkt->data; + break; + case JD_GET(JD_INDEXED_SCREEN_REG_WIDTH): + screenWidth = *(uint16_t *)pkt->data; + break; + } + } else if (controlsStartServiceNum <= pkt->service_number && + pkt->service_number <= controlsEndServiceNum && + pkt->service_command == (JD_CMD_GET_REG | JD_REG_READING)) { + auto report = (jd_arcade_gamepad_buttons_t *)pkt->data; + auto endp = pkt->data + pkt->service_size; + uint32_t state = 0; + + while ((uint8_t *)report < endp) { + int idx = 0; + int b = report->button; + + if (report->pressure < 0x20) + continue; + + if (b == JD_ARCADE_GAMEPAD_BUTTON_SELECT) + b = JD_ARCADE_GAMEPAD_BUTTON_MENU; + + if (b == JD_ARCADE_GAMEPAD_BUTTON_RESET || b == JD_ARCADE_GAMEPAD_BUTTON_EXIT) + target_reset(); + + if (1 <= b && b <= 7) { + idx = b + 7 * (pkt->service_number - controlsStartServiceNum); + } + + if (idx > 0) + state |= 1 << idx; + + report++; + } + + if (state != buttonState) { + for (int i = 0; i < 32; ++i) { + if ((state & (1 << i)) && !(buttonState & (1 << i))) + Event(PXT_INTERNAL_KEY_DOWN, i); + if (!(state & (1 << i)) && (buttonState & (1 << i))) + Event(PXT_INTERNAL_KEY_UP, i); + } + buttonState = state; + } + } else { + // TODO remove later + VLOG("JDA: unknown packet for %d (cmd=%x)", pkt->service_number, pkt->service_command); + } +} + +void JDDisplay::step() { + if (cs) + cs->setDigitalValue(1); + + target_disable_irq(); + if (!flow->getDigitalValue()) { + stepWaiting = true; + target_enable_irq(); + return; + } else { + stepWaiting = false; + } + target_enable_irq(); + + memset(&sendFrame, 0, JD_SERIAL_FULL_HEADER_SIZE); + sendFrame.crc = JDSPI_MAGIC; + sendFrame.device_identifier = device.getSerialNumber(); + + if (recvFrame.crc == JDSPI_MAGIC_NOOP) { + // empty frame, skip + } else if (recvFrame.crc != JDSPI_MAGIC) { + DMESG("JDA: magic mismatch %x", (int)recvFrame.crc); + } else if (recvFrame.size == 0) { + // empty frame, skip + } else { + for (;;) { + handleIncoming((jd_packet_t *)&recvFrame); + if (!jd_shift_frame(&recvFrame)) + break; + } + } + + if (displayServiceNum == 0) { + // poke the control service to enumerate + queuePkt(JD_SERVICE_NUMBER_CTRL, JD_CMD_ADVERTISEMENT_DATA, 0); + flushSend(); + return; + } + + if (palette) { + { +#define PALETTE_SIZE (16 * 4) + auto cmd = + queuePkt(displayServiceNum, JD_SET(JD_INDEXED_SCREEN_REG_PALETTE), PALETTE_SIZE); + memcpy(cmd, palette, PALETTE_SIZE); + palette = NULL; + } + { + auto cmd = (jd_indexed_screen_start_update_t *)queuePkt( + displayServiceNum, JD_INDEXED_SCREEN_CMD_START_UPDATE, + sizeof(jd_indexed_screen_start_update_t)); + *cmd = this->addr; + } + { + auto cmd = + (uint8_t *)queuePkt(displayServiceNum, JD_SET(JD_INDEXED_SCREEN_REG_BRIGHTNESS), 1); + *cmd = this->brightness * 0xff / 100; + } + + if (soundServiceNum) { + // we only need this for sending sound + uint32_t now = (uint32_t)(pxt::current_time_ms()); + if (lastFrameTimestamp) { + uint32_t thisFrame = now - lastFrameTimestamp; + avgFrameTime = (avgFrameTime * 15 + thisFrame) >> 4; + } + lastFrameTimestamp = now; + // send around 2 frames of sound; typically around 60ms, so ~3000 samples + soundBufferDesiredSize = + sizeof(int16_t *) * ((((avgFrameTime * 2) >> 10) * soundSampleRate) >> 10); + } + + flushSend(); + return; + } + + if (dataLeft > 0) { + uint32_t transfer = bytesPerTransfer; + if (dataLeft < transfer) + transfer = dataLeft; + auto pixels = queuePkt(displayServiceNum, JD_INDEXED_SCREEN_CMD_SET_PIXELS, transfer); + memcpy(pixels, dataPtr, transfer); + dataPtr += transfer; + dataLeft -= transfer; + flushSend(); + } else if (soundServiceNum && soundBufferPending < soundBufferDesiredSize) { + int bytesLeft = soundBufferDesiredSize - soundBufferPending; + if (bytesLeft > bytesPerTransfer) + bytesLeft = bytesPerTransfer; + auto samples = (int16_t *)queuePkt(soundServiceNum, JD_ARCADE_SOUND_CMD_PLAY, bytesLeft); + if (pxt::redirectSamples(samples, bytesLeft >> 1, soundSampleRate)) { + soundBufferPending += bytesLeft; + } else { + // no sound generated, fill with 0 and stop + memset(samples, 0, bytesLeft); + soundBufferDesiredSize = 0; + } + flushSend(); + } else { + // trigger sendDone(), which executes outside of IRQ context, so there + // is no race with waitForSendDone + Event(DEVICE_ID_DISPLAY, 4243); + } +} + +int JDDisplay::sendIndexedImage(const uint8_t *src, unsigned width, unsigned height, + uint32_t *palette) { + if (height & 1 || !height || !width) + target_panic(123); // PANIC_SCREEN_ERROR + if (width != addr.width || height != addr.height) + target_panic(124); // PANIC_SCREEN_ERROR + if (inProgress) + target_panic(125); // PANIC_SCREEN_ERROR + + if (addr.y && addr.y >= screenHeight) + return 0; // out of range + + inProgress = true; + + int numcols = JD_SERIAL_PAYLOAD_SIZE / (height / 2); + + bytesPerTransfer = numcols * (height / 2); + dataLeft = (height / 2) * width; + dataPtr = src; + + this->palette = palette; + + memset(&sendFrame, 0, sizeof(sendFrame)); + + step(); + + return 0; +} + +} // namespace pxt \ No newline at end of file diff --git a/libs/arcadeshield/jddisplay.h b/libs/arcadeshield/jddisplay.h new file mode 100644 index 00000000000..f9626d388be --- /dev/null +++ b/libs/arcadeshield/jddisplay.h @@ -0,0 +1,67 @@ +#ifndef __JDDISPLAY_H +#define __JDDISPLAY_H + +#include "pxt.h" + +#include "Pin.h" +#define PinCompat codal::Pin + +#undef SPI +#include "jdprotocol.h" +#include "arcadegamepad.h" +#include "indexedscreen.h" +#include "arcadesound.h" + +namespace pxt { + +class JDDisplay { + jd_indexed_screen_start_update_t addr; + SPI *spi; + Pin *cs; + Pin *flow; + uint32_t dataLeft; + const uint8_t *dataPtr; + uint32_t *palette; + jd_frame_t sendFrame; + jd_frame_t recvFrame; + uint8_t bytesPerTransfer; + bool inProgress; + volatile bool stepWaiting; + uint8_t displayServiceNum; + uint8_t controlsStartServiceNum; + uint8_t controlsEndServiceNum; + uint8_t soundServiceNum; + uint16_t screenWidth, screenHeight; + uint32_t buttonState; + uint32_t avgFrameTime; // in us + uint32_t lastFrameTimestamp; + + uint32_t soundBufferDesiredSize; + uint32_t soundBufferPending; + uint16_t soundSampleRate; + + void *queuePkt(uint32_t service_num, uint32_t service_cmd, uint32_t size); + void flushSend(); + void step(); + void sendDone(Event); + static void stepStatic(void *); + void onFlowHi(Event); + void handleIncoming(jd_packet_t *pkt); + + public: + uint8_t brightness; + JDDisplay(SPI *spi, Pin *cs, Pin *flow); + void setAddrWindow(int x, int y, int w, int h) { + addr.x = x; + addr.y = y; + addr.width = w; + addr.height = h; + } + void waitForSendDone(); + + int sendIndexedImage(const uint8_t *src, unsigned width, unsigned height, uint32_t *palette); +}; + +} // namespace pxt + +#endif diff --git a/libs/arcadeshield/jdprotocol.h b/libs/arcadeshield/jdprotocol.h new file mode 100644 index 00000000000..8d81c488e0d --- /dev/null +++ b/libs/arcadeshield/jdprotocol.h @@ -0,0 +1,125 @@ +#ifndef __JDPROTOCOL_H +#define __JDPROTOCOL_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// 255 minus size of the serial header, rounded down to 4 +#define JD_SERIAL_PAYLOAD_SIZE 236 +#define JD_SERIAL_FULL_HEADER_SIZE 16 + +#define JD_SERVICE_CLASS_CTRL 0x00000000 + +#define JD_SERVICE_NUMBER_CTRL 0x00 +#define JD_SERVICE_NUMBER_MASK 0x3f +#define JD_SERVICE_NUMBER_CRC_ACK 0x3f + +// the COMMAND flag signifies that the device_identifier is the recipent +// (i.e., it's a command for the peripheral); the bit clear means device_identifier is the source +// (i.e., it's a report from peripheral or a broadcast message) +#define JD_FRAME_FLAG_COMMAND 0x01 +// an ACK should be issued with CRC of this package upon reception +#define JD_FRAME_FLAG_ACK_REQUESTED 0x02 +// the device_identifier contains target service class number +#define JD_FRAME_FLAG_IDENTIFIER_IS_SERVICE_CLASS 0x04 + +#define JD_FRAME_SIZE(pkt) ((pkt)->size + 12) + +// Registers 0x001-0x07f - r/w common to all services +// Registers 0x080-0x0ff - r/w defined per-service +// Registers 0x100-0x17f - r/o common to all services +// Registers 0x180-0x1ff - r/o defined per-service +// Registers 0x200-0xeff - custom, defined per-service +// Registers 0xf00-0xfff - reserved for implementation, should not be on the wire + +// this is either binary (0 or non-zero), or can be gradual (eg. brightness of neopixel) +#define JD_REG_INTENSITY 0x01 +// the primary value of actuator (eg. servo angle) +#define JD_REG_VALUE 0x02 +// enable/disable streaming +#define JD_REG_IS_STREAMING 0x03 +// streaming interval in miliseconds +#define JD_REG_STREAMING_INTERVAL 0x04 +// for analog sensors +#define JD_REG_LOW_THRESHOLD 0x05 +#define JD_REG_HIGH_THRESHOLD 0x06 +// limit power drawn; in mA +#define JD_REG_MAX_POWER 0x07 + +// eg. one number for light sensor, all 3 coordinates for accelerometer +#define JD_REG_READING 0x101 + +#define JD_CMD_GET_REG 0x1000 +#define JD_CMD_SET_REG 0x2000 + +#define JD_GET(reg) (JD_CMD_GET_REG | (reg)) +#define JD_SET(reg) (JD_CMD_SET_REG | (reg)) + +// Commands 0x000-0x07f - common to all services +// Commands 0x080-0xeff - defined per-service +// Commands 0xf00-0xfff - reserved for implementation +// enumeration data for CTRL, ad-data for other services +#define JD_CMD_ADVERTISEMENT_DATA 0x00 +// event from sensor or on broadcast service +#define JD_CMD_EVENT 0x01 +// request to calibrate sensor +#define JD_CMD_CALIBRATE 0x02 +// request human-readable description of service +#define JD_CMD_GET_DESCRIPTION 0x03 + +// Commands specific to control service +// do nothing +#define JD_CMD_CTRL_NOOP 0x80 +// blink led or otherwise draw user's attention +#define JD_CMD_CTRL_IDENTIFY 0x81 +// reset device +#define JD_CMD_CTRL_RESET 0x82 +// identifies the type of hardware (eg., ACME Corp. Servo X-42 Rev C) +#define JD_REG_CTRL_DEVICE_DESCRIPTION 0x180 +// a numeric code for the string above; used to mark firmware images +#define JD_REG_CTRL_DEVICE_CLASS 0x181 +// MCU temperature in Celsius +#define JD_REG_CTRL_TEMPERATURE 0x182 +// this is very approximate; ADC reading from backward-biasing the identification LED +#define JD_REG_CTRL_LIGHT_LEVEL 0x183 +// typically the same as JD_REG_CTRL_DEVICE_CLASS; the bootloader will respond to that code +#define JD_REG_CTRL_BL_DEVICE_CLASS 0x184 + +struct _jd_packet_t { + uint16_t crc; + uint8_t _size; // of frame data[] + uint8_t flags; + + uint64_t device_identifier; + + uint8_t service_size; + uint8_t service_number; + uint16_t service_command; + + uint8_t data[0]; +} __attribute__((__packed__, aligned(4))); +typedef struct _jd_packet_t jd_packet_t; + +struct _jd_frame_t { + uint16_t crc; + uint8_t size; + uint8_t flags; + + uint64_t device_identifier; + + uint8_t data[JD_SERIAL_PAYLOAD_SIZE + 4]; +} __attribute__((__packed__, aligned(4))); +typedef struct _jd_frame_t jd_frame_t; + +#define JDSPI_MAGIC 0x7ACD +#define JDSPI_MAGIC_NOOP 0xB3CD + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/arcadeshield/pinsDigital.cpp b/libs/arcadeshield/pinsDigital.cpp new file mode 100644 index 00000000000..042a986e1aa --- /dev/null +++ b/libs/arcadeshield/pinsDigital.cpp @@ -0,0 +1,142 @@ +#include "pxt.h" + +#include "Pin.h" +#define PinCompat codal::Pin + +#include "config_nrf.h" + +namespace pxt { + +static void waitABit() { + // for (int i = 0; i < 10; ++i) + // asm volatile("nop"); +} + +class ButtonMultiplexer : public CodalComponent { + public: + Pin &latch; + Pin &clock; + Pin &data; + uint32_t state; + uint32_t invMask; + uint16_t buttonIdPerBit[8]; + bool enabled; + + ButtonMultiplexer(uint16_t id) + : latch(uBit.io.P9), + clock(uBit.io.P20), + data((uBit.io.P14)) { + this->id = id; + this->status |= DEVICE_COMPONENT_STATUS_SYSTEM_TICK; + + state = 0; + invMask = 0; + enabled = true; + + memset(buttonIdPerBit, 0, sizeof(buttonIdPerBit)); + + data.setPull(PullMode::Down); + data.getDigitalValue(); + latch.setDigitalValue(1); + clock.setDigitalValue(1); + } + + void disable() { + data.getDigitalValue(PullMode::None); + latch.getDigitalValue(PullMode::None); + clock.getDigitalValue(PullMode::None); + enabled = false; + } + + bool isButtonPressed(int id) { + for (int i = 0; i < 8; ++i) { + if (buttonIdPerBit[i] == id) + return (state & (1 << i)) != 0; + } + return false; + } + + uint32_t readBits(int bits) { + latch.setDigitalValue(0); + waitABit(); + latch.setDigitalValue(1); + waitABit(); + + uint32_t state = 0; + for (int i = 0; i < bits; i++) { + state <<= 1; + if (data.getDigitalValue()) + state |= 1; + + clock.setDigitalValue(0); + waitABit(); + clock.setDigitalValue(1); + waitABit(); + } + + return state; + } + + virtual void periodicCallback() override { + if (!enabled) + return; + + uint32_t newState = readBits(8); + newState ^= invMask; + if (newState == state) + return; + + for (int i = 0; i < 8; ++i) { + uint32_t mask = 1 << i; + if (!buttonIdPerBit[i]) + continue; + int ev = 0; + if (!(state & mask) && (newState & mask)) + ev = PXT_INTERNAL_KEY_DOWN; + else if ((state & mask) && !(newState & mask)) + ev = PXT_INTERNAL_KEY_UP; + if (ev) { + Event(ev, buttonIdPerBit[i]); + Event(ev, 0); // any key + } + } + + state = newState; + } +}; + +static ButtonMultiplexer *btnMultiplexer; +ButtonMultiplexer *getMultiplexer() { + if (!btnMultiplexer) + btnMultiplexer = new ButtonMultiplexer(DEVICE_ID_FIRST_BUTTON); + return btnMultiplexer; +} + +int registerMultiplexedButton(int pin, int buttonId) { + if (1050 <= pin && pin < 1058) { + pin -= 50; + getMultiplexer()->invMask |= 1 << (pin - 1000); + } + if (1000 <= pin && pin < 1008) { + getMultiplexer()->buttonIdPerBit[pin - 1000] = buttonId; + return 1; + } + return 0; +} + +int multiplexedButtonIsPressed(int btnId) { + if (btnMultiplexer) + return btnMultiplexer->isButtonPressed(btnId) ? 512 : 0; + return 0; +} + +uint32_t readButtonMultiplexer(int bits) { + return getMultiplexer()->readBits(bits); +} + +void disableButtonMultiplexer() { + getMultiplexer()->disable(); +} + +} + diff --git a/libs/arcadeshield/pxt.json b/libs/arcadeshield/pxt.json new file mode 100644 index 00000000000..bd04ee673aa --- /dev/null +++ b/libs/arcadeshield/pxt.json @@ -0,0 +1,35 @@ +{ + "name": "arcadeshield", + "files": [ + "config_nrf.h", + "screen.cpp", + "image.cpp", + "image.ts", + "screenimage.ts", + "text.ts", + "frame.ts", + "shims.d.ts", + "fieldeditors.ts", + "targetoverrides.ts", + "ns.ts", + "image.d.ts", + "pxtparts.json", + "imagesoverrides.jres", + "imagesoverrides.ts", + "font12.jres", + "arcadegamepad.h", + "indexedscreen.h", + "arcadesound.h", + "jdprotocol.h", + "jddisplay.h", + "jddisplay.cpp", + "pinsDigital.cpp" + ], + "dependencies": { + "core": "file:../core" + }, + "additionalFilePath": "../screen", + "disablesVariants": [ + "mbdal" + ] +} diff --git a/libs/arcadeshield/screen.cpp b/libs/arcadeshield/screen.cpp new file mode 100644 index 00000000000..fde0bc0c689 --- /dev/null +++ b/libs/arcadeshield/screen.cpp @@ -0,0 +1,371 @@ +#include "pxt.h" + +#include "Pin.h" +#define PinCompat codal::Pin + +#include "ST7735.h" +#include "ILI9341.h" + +// this is a hack because someone (don't know where) #defined SPI to be NRF52SPI, +// which messes with the include file below the #undef +#undef SPI +#include "SPIScreenIO.h" + +#include "jddisplay.h" + +#include "config_nrf.h" + +typedef RefImage *SImage_; + +namespace pxt { + +class WDisplay { + public: + ScreenIO *io; + ST7735 *lcd; + JDDisplay *smart; + + uint32_t currPalette[16]; + bool newPalette; + bool inUpdate; + + uint8_t *screenBuf; + SImage_ lastStatus; + + uint16_t width, height; + uint16_t displayHeight; + uint8_t offX, offY; + bool doubleSize; + uint32_t palXOR; + + WDisplay() { + uint32_t cfg2 = MY_DISPLAY_CFG2; + + uint32_t cfg0 = MY_DISPLAY_CFG0; + uint32_t frmctr1 = MY_DISPLAY_CFG1; + + int dispTp = MY_DISPLAY_TYPE; + + doubleSize = false; + smart = NULL; + + auto miso = LOOKUP_PIN(DISPLAY_MISO); + + if (dispTp == DISPLAY_TYPE_SMART) { + dispTp = smartConfigure(&cfg0, &frmctr1, &cfg2); + } + + if (dispTp != DISPLAY_TYPE_SMART) + miso = NULL; // only JDDisplay needs MISO, otherwise leave free + + SPI *spi = new CODAL_SPI(*LOOKUP_PIN(DISPLAY_MOSI), *miso, *LOOKUP_PIN(DISPLAY_SCK)); + io = new SPIScreenIO(*spi); + + if (dispTp == DISPLAY_TYPE_ST7735) { + lcd = new ST7735(*io, *LOOKUP_PIN(DISPLAY_CS), *LOOKUP_PIN(DISPLAY_DC)); + } else if (dispTp == DISPLAY_TYPE_ILI9341) { + lcd = new ILI9341(*io, *LOOKUP_PIN(DISPLAY_CS), *LOOKUP_PIN(DISPLAY_DC)); + doubleSize = true; + } else if (dispTp == DISPLAY_TYPE_SMART) { + lcd = NULL; + smart = new JDDisplay(spi, LOOKUP_PIN(DISPLAY_CS), LOOKUP_PIN(DISPLAY_DC)); + } else + target_panic(128); // PANIC_SCREEN_ERROR + + palXOR = (cfg0 & 0x1000000) ? 0xffffff : 0x000000; + auto madctl = cfg0 & 0xff; + offX = (cfg0 >> 8) & 0xff; + offY = (cfg0 >> 16) & 0xff; + + DMESG("configure screen: FRMCTR1=%p MADCTL=%p type=%d", frmctr1, madctl, dispTp); + + if (spi) { + auto freq = (cfg2 & 0xff); + if (!freq) + freq = 15; + spi->setFrequency(freq * 1000000); + spi->setMode(0); + // make sure the SPI peripheral is initialized before toggling reset + spi->write(0); + } + + auto rst = LOOKUP_PIN(DISPLAY_RST); + if (rst) { + rst->setDigitalValue(0); + fiber_sleep(20); + rst->setDigitalValue(1); + fiber_sleep(20); + } + + if (lcd) { + auto bl = LOOKUP_PIN(DISPLAY_BL); + if (bl) { + bl->setDigitalValue(1); + } + + lcd->init(); + lcd->configure(madctl, frmctr1); + } + + width = MY_DISPLAY_WIDTH; + height = MY_DISPLAY_HEIGHT; + displayHeight = height; + setAddrMain(); + DMESG("screen: %d x %d, off=%d,%d", width, height, offX, offY); + int sz = doubleSize ? (width >> 1) * (height >> 1) : width * height; + screenBuf = (uint8_t *)app_alloc(sz / 2 + 20); + + lastStatus = NULL; + registerGC((TValue *)&lastStatus); + inUpdate = false; + } + + uint32_t smartConfigure(uint32_t *cfg0, uint32_t *cfg1, uint32_t *cfg2) { + uint32_t hc; + + DMESG("74HC: waiting..."); + + // wait while nothing is connected + for (;;) { + auto rst = LOOKUP_PIN(DISPLAY_RST); + if (rst) { + rst->setDigitalValue(0); + target_wait_us(10); + rst->setDigitalValue(1); + fiber_sleep(3); // in reality we need around 1.2ms + } + + hc = readButtonMultiplexer(17); + if (hc != 0) + break; + + fiber_sleep(100); + + // the device will run without shield when the following is specified in user program: + // namespace userconfig { export const DISPLAY_CFG0 = 0x02000080 } + if (*cfg0 & 0x2000000) { + DMESG("74HC: no wait requested"); + return DISPLAY_TYPE_ST7735; + } + } + + DMESG("74HC: %x", hc); + + // is the line forced up? if so, assume JDDisplay + if (hc == 0x1FFFF) { + disableButtonMultiplexer(); + return DISPLAY_TYPE_SMART; + } + + hc = hc >> 1; + + // SER pin (or first bit of second HC) is orientation + if (hc & 0x0010) + *cfg0 = 0x80; + else + *cfg0 = 0x40; + + uint32_t configId = (hc & 0xe0) >> 5; + + switch (configId) { + case 1: + *cfg1 = 0x0603; // ST7735 + break; + case 2: + *cfg1 = 0xe14ff; // ILI9163C + *cfg0 |= 0x08; // BGR colors + break; + case 3: + *cfg1 = 0x0603; // ST7735 + *cfg0 |= 0x1000000; // inverted colors + break; + default: + target_panic(129); // PANIC_SCREEN_ERROR + break; + } + + DMESG("config type: %d; cfg0=%x cfg1=%x", configId, *cfg0, *cfg1); + + // for some reason, setting SPI frequency to 32 doesn't + // work with ST77735 in pxt-microbit + *cfg2 = 16; // Damn the torpedoes! 32MHz + + return DISPLAY_TYPE_ST7735; + } + + void setAddrStatus() { + if (lcd) + lcd->setAddrWindow(offX, offY + displayHeight, width, height - displayHeight); + else + smart->setAddrWindow(offX, offY + displayHeight, width, height - displayHeight); + } + void setAddrMain() { + if (lcd) + lcd->setAddrWindow(offX, offY, width, displayHeight); + else + smart->setAddrWindow(offX, offY, width, displayHeight); + } + void waitForSendDone() { + if (lcd) + lcd->waitForSendDone(); + else + smart->waitForSendDone(); + } + int sendIndexedImage(const uint8_t *src, unsigned width, unsigned height, uint32_t *palette) { + if (lcd) + return lcd->sendIndexedImage(src, width, height, palette); + else + return smart->sendIndexedImage(src, width, height, palette); + } +}; + +SINGLETON_IF_PIN(WDisplay, DISPLAY_MOSI); + +//% +int setScreenBrightnessSupported() { + auto display = getWDisplay(); + if (display && display->smart) + return 1; + + auto bl = LOOKUP_PIN(DISPLAY_BL); + if (!bl) + return 0; +#ifdef SAMD51 + if (bl->name == PA06) + return 0; +#endif +#ifdef NRF52_SERIES + // PWM not implemented yet + return 0; +#else + return 1; +#endif +} + +//% +void setScreenBrightness(int level) { + if (level < 0) + level = 0; + if (level > 100) + level = 100; + + auto display = getWDisplay(); + if (display && display->smart) { + display->smart->brightness = level; + return; + } + + auto bl = LOOKUP_PIN(DISPLAY_BL); + if (!bl) + return; + + if (level == 0) + bl->setDigitalValue(0); + else if (level == 100) + bl->setDigitalValue(1); + else { + if (setScreenBrightnessSupported()) { + bl->setAnalogPeriodUs(1000); + bl->setAnalogValue(level * level * 1023 / 10000); + } + } +} + +//% +void setPalette(Buffer buf) { + auto display = getWDisplay(); + if (!display) + return; + + if (48 != buf->length) + target_panic(130); // PANIC_SCREEN_ERROR + for (int i = 0; i < 16; ++i) { + display->currPalette[i] = + (buf->data[i * 3] << 16) | (buf->data[i * 3 + 1] << 8) | (buf->data[i * 3 + 2] << 0); + display->currPalette[i] ^= display->palXOR; + } + display->newPalette = true; +} + +//% +void setupScreenStatusBar(int barHeight) { + auto display = getWDisplay(); + if (!display) + return; + if (!display->doubleSize) { + display->displayHeight = display->height - barHeight; + display->setAddrMain(); + } +} + +//% +void updateScreenStatusBar(SImage_ img) { + auto display = getWDisplay(); + if (!display) + return; + + if (!img) + return; + display->lastStatus = img; +} + +//% +void updateScreen(SImage_ img) { + auto display = getWDisplay(); + if (!display) + return; + + if (display->inUpdate) + return; + + display->inUpdate = true; + + auto mult = display->doubleSize ? 2 : 1; + + if (img) { + if (img->bpp() != 4 || img->width() * mult != display->width || + img->height() * mult != display->displayHeight) + target_panic(131); // PANIC_SCREEN_ERROR + + // DMESG("wait for done"); + display->waitForSendDone(); + + auto palette = display->currPalette; + + if (display->newPalette) { + display->newPalette = false; + } else { + // smart mode always sends palette + if (!display->smart) + palette = NULL; + } + + memcpy(display->screenBuf, img->pix(), img->pixLength()); + + // DMESG("send"); + display->sendIndexedImage(display->screenBuf, img->width(), img->height(), palette); + } + + if (display->lastStatus && !display->doubleSize) { + display->waitForSendDone(); + img = display->lastStatus; + auto barHeight = display->height - display->displayHeight; + if (img->bpp() != 4 || barHeight != img->height() || img->width() != display->width) + target_panic(132); // PANIC_SCREEN_ERROR + memcpy(display->screenBuf, img->pix(), img->pixLength()); + display->setAddrStatus(); + display->sendIndexedImage(display->screenBuf, img->width(), img->height(), NULL); + display->waitForSendDone(); + display->setAddrMain(); + display->lastStatus = NULL; + } + + display->inUpdate = false; +} + +//% +void updateStats(String msg) { + // ignore... +} + +} // namespace pxt \ No newline at end of file diff --git a/libs/arcadeshield/shims.d.ts b/libs/arcadeshield/shims.d.ts new file mode 100644 index 00000000000..7da7294cc02 --- /dev/null +++ b/libs/arcadeshield/shims.d.ts @@ -0,0 +1,153 @@ +// Auto-generated. Do not edit. + + +declare interface SImage { + /** + * Get the width of the image + */ + //% property shim=SImageMethods::width + width: int32; + + /** + * Get the height of the image + */ + //% property shim=SImageMethods::height + height: int32; + + /** + * True if the image is monochromatic (black and white) + */ + //% property shim=SImageMethods::isMono + isMono: boolean; + + /** + * Sets all pixels in the current image from the other image, which has to be of the same size and + * bpp. + */ + //% shim=SImageMethods::copyFrom + copyFrom(from: SImage): void; + + /** + * Set pixel color + */ + //% shim=SImageMethods::setPixel + setPixel(x: int32, y: int32, c: int32): void; + + /** + * Get a pixel color + */ + //% shim=SImageMethods::getPixel + getPixel(x: int32, y: int32): int32; + + /** + * Fill entire image with a given color + */ + //% shim=SImageMethods::fill + fill(c: int32): void; + + /** + * Copy row(s) of pixel from image to buffer (8 bit per pixel). + */ + //% shim=SImageMethods::getRows + getRows(x: int32, dst: Buffer): void; + + /** + * Copy row(s) of pixel from buffer to image. + */ + //% shim=SImageMethods::setRows + setRows(x: int32, src: Buffer): void; + + /** + * Return a copy of the current image + */ + //% shim=SImageMethods::clone + clone(): SImage; + + /** + * Flips (mirrors) pixels horizontally in the current image + */ + //% shim=SImageMethods::flipX + flipX(): void; + + /** + * Flips (mirrors) pixels vertically in the current image + */ + //% shim=SImageMethods::flipY + flipY(): void; + + /** + * Returns a transposed image (with X/Y swapped) + */ + //% shim=SImageMethods::transposed + transposed(): SImage; + + /** + * Every pixel in image is moved by (dx,dy) + */ + //% shim=SImageMethods::scroll + scroll(dx: int32, dy: int32): void; + + /** + * Stretches the image horizontally by 100% + */ + //% shim=SImageMethods::doubledX + doubledX(): SImage; + + /** + * Stretches the image vertically by 100% + */ + //% shim=SImageMethods::doubledY + doubledY(): SImage; + + /** + * Replaces one color in an image with another + */ + //% shim=SImageMethods::replace + replace(from: int32, to: int32): void; + + /** + * Stretches the image in both directions by 100% + */ + //% shim=SImageMethods::doubled + doubled(): SImage; + + /** + * Draw given image on the current image + */ + //% shim=SImageMethods::drawImage + drawImage(from: SImage, x: int32, y: int32): void; + + /** + * Draw given image with transparent background on the current image + */ + //% shim=SImageMethods::drawTransparentImage + drawTransparentImage(from: SImage, x: int32, y: int32): void; + + /** + * Check if the current image "collides" with another + */ + //% shim=SImageMethods::overlapsWith + overlapsWith(other: SImage, x: int32, y: int32): boolean; +} +declare namespace simage { + + /** + * Create new empty (transparent) image + */ + //% shim=simage::create + function create(width: int32, height: int32): SImage; + + /** + * Create new image with given content + */ + //% shim=simage::ofBuffer + function ofBuffer(buf: Buffer): SImage; + + /** + * Double the size of an icon + */ + //% shim=simage::doubledIcon + function doubledIcon(icon: Buffer): Buffer; +} + +// Auto-generated. Do not edit. Really. diff --git a/libs/arcadeshield/targetoverrides.ts b/libs/arcadeshield/targetoverrides.ts new file mode 100644 index 00000000000..22f82766a27 --- /dev/null +++ b/libs/arcadeshield/targetoverrides.ts @@ -0,0 +1,38 @@ +/** + * Tagged image literal converter + */ +//% shim=@f4 helper=image::ofBuffer blockIdentity="sprites._createImageShim" +//% groups=["0.","1#","2T","3t","4N","5n","6G","7g","8","9","aAR","bBP","cCp","dDO","eEY","fFW"] +function img(lits: any, ...args: any[]): SImage { return null } + +// set palette before creating screen, so the JS version has the right BPP +simage.setPalette(hex`000000ffffffff2121ff93c4ff8135fff609249ca378dc52003fad87f2ff8e2ec4a4839f5c406ce5cdc491463d000000`) + +//% whenUsed +const screen = _screen_internal.createScreen(); + +namespace simage { + //% shim=pxt::setPalette + export function setPalette(buf: Buffer) { } +} + +namespace _screen_internal { + //% shim=pxt::updateScreen + function updateScreen(img: SImage): void { } + //% shim=pxt::updateStats + function updateStats(msg: string): void { } + + //% parts="screen" + export function createScreen() { + const img = simage.create( + 160, // control.getConfigValue(DAL.CFG_DISPLAY_WIDTH, 160), + 128 // control.getConfigValue(DAL.CFG_DISPLAY_HEIGHT, 128)) + ) + control.__screen.setupUpdate(() => updateScreen(img)) + //control.EventContext.onStats = function (msg: string) { + // updateStats(msg); + //} + + return img as ScreenImage; + } +} diff --git a/libs/blocksprj/pxt.json b/libs/blocksprj/pxt.json index 4c6ab6e8b80..192c92a7b1e 100644 --- a/libs/blocksprj/pxt.json +++ b/libs/blocksprj/pxt.json @@ -3,7 +3,8 @@ "dependencies": { "core": "file:../core", "radio": "file:../radio", - "microphone": "file:../microphone" + "microphone": "file:../microphone", + "arcadeshield": "file:../arcadeshield" }, "description": "", "files": [ diff --git a/libs/bluetoothprj/README.md b/libs/bluetoothprj/README.md deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/libs/bluetoothprj/main.blocks b/libs/bluetoothprj/main.blocks deleted file mode 100644 index cf17f1bb950..00000000000 --- a/libs/bluetoothprj/main.blocks +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/libs/bluetoothprj/main.ts b/libs/bluetoothprj/main.ts deleted file mode 100644 index 8b137891791..00000000000 --- a/libs/bluetoothprj/main.ts +++ /dev/null @@ -1 +0,0 @@ - diff --git a/libs/bluetoothprj/pxt.json b/libs/bluetoothprj/pxt.json deleted file mode 100644 index 6315723926e..00000000000 --- a/libs/bluetoothprj/pxt.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "{0} block", - "dependencies": { - "core": "file:../core", - "bluetooth": "file:../bluetooth", - "microphone": "file:../microphone" - }, - "description": "", - "files": [ - "main.blocks", - "main.ts", - "README.md" - ] -} diff --git a/libs/bluetoothprj/tsconfig.json b/libs/bluetoothprj/tsconfig.json deleted file mode 100644 index 1ba59f2cc36..00000000000 --- a/libs/bluetoothprj/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "noImplicitAny": true, - "outDir": "built", - "rootDir": "." - } -} diff --git a/libs/core/pxtcore.h b/libs/core/pxtcore.h index 14af8637f86..16db048f891 100644 --- a/libs/core/pxtcore.h +++ b/libs/core/pxtcore.h @@ -14,18 +14,19 @@ void debuglog(const char *format, ...); #define xmalloc malloc #define xfree free -#define GC_MAX_ALLOC_SIZE 9000 - #define NON_GC_HEAP_RESERVATION 1024 #ifdef CODAL_CONFIG_H #define MICROBIT_CODAL 1 #else #define MICROBIT_CODAL 0 -#define GC_BLOCK_SIZE 256 #endif -#if !MICROBIT_CODAL +#if MICROBIT_CODAL +#define GC_MAX_ALLOC_SIZE 11000 +#else +#define GC_BLOCK_SIZE 256 +#define GC_MAX_ALLOC_SIZE 9000 #undef DMESG #define DMESG NOLOG #endif diff --git a/libs/game---light/pxt.json b/libs/game---light/pxt.json new file mode 100644 index 00000000000..c41ad1fb79e --- /dev/null +++ b/libs/game---light/pxt.json @@ -0,0 +1,27 @@ + +{ + "hidden": true, + "name": "game---light", + "description": "Empty game library - beta", + "files": [ + "compat.ts", + "console.ts", + "constants.ts", + "controlleroverrides.ts", + "controllerbutton.ts", + "controllerbuttons.cpp", + "mathUtil.ts", + "gameutil.ts", + "targetoverrides.cpp", + "targetoverrides.ts" + ], + "public": true, + "additionalFilePath": "../game", + "dependencies": { + "settings": "file:../settings", + "screen": "file:../arcadeshield" + }, + "disablesVariants": [ + "mbdal" + ] +} diff --git a/libs/game/pxt.json b/libs/game/pxt.json new file mode 100644 index 00000000000..6ee42f3605d --- /dev/null +++ b/libs/game/pxt.json @@ -0,0 +1,7 @@ +{ + "hidden": true, + "disablesVariants": [ + "mbdal" + ], + "additionalFilePath": "../../node_modules/pxt-common-packages/libs/game" +} \ No newline at end of file diff --git a/libs/microcode/accessibility.ts b/libs/microcode/accessibility.ts new file mode 100644 index 00000000000..deb32eb8032 --- /dev/null +++ b/libs/microcode/accessibility.ts @@ -0,0 +1,48 @@ +namespace accessibility { + export interface AccessibilityMessage { + type: "text" | "tile" | "rule" | "led" | "note" + force?: boolean + } + + export interface TextAccessibilityMessage extends AccessibilityMessage { + type: "text" + value: string + } + + export interface LEDAccessibilityMessage extends AccessibilityMessage { + type: "led" + on: boolean + x: number + y: number + } + + export interface NoteAccessibilityMessage extends AccessibilityMessage { + type: "note" + on: boolean + index: number + } + + export interface TileAccessibilityMessage extends AccessibilityMessage { + type: "tile" + value: string + } + + export interface RuleAccessibilityMessage extends AccessibilityMessage { + type: "rule" + dos: string[] + whens: string[] + } + + /** + * Notifies web application about the current content. + */ + //% shim=TD_ID + export function setLiveContent(msg: AccessibilityMessage) { + const data = Buffer.fromUTF8(JSON.stringify(msg)) + control.simmessages.send("accessibility", data) + } + + export function ariaToTooltip(ariaId: string) { + return microcode.resolveTooltip(ariaId).replaceAll("_", " ") + } +} diff --git a/libs/microcode/affine.ts b/libs/microcode/affine.ts new file mode 100644 index 00000000000..0171e3a8257 --- /dev/null +++ b/libs/microcode/affine.ts @@ -0,0 +1,74 @@ +namespace microcode { + /** + * An Affine represents a euclidean transformation on a Vec2. + * At the moment this class only supports translation, but if needed it could easily include: + * - Rotation + * - Scaling + * - Skew (less common) + * Affine transformations can be chained thru the `parent` property, in which case `worldPos` will report the composed value. + */ + export class Affine { + private localPos_: Vec2 + private parent_: Affine + + //% blockCombine block="worldPos" callInDebugger + public get worldPos() { + return this.computeWorldPos() + } + + //% blockCombine block="localPos" callInDebugger + public get localPos(): Vec2 { + return this.localPos_ + } + public set localPos(v: Vec2) { + this.localPos_.copyFrom(v) + } + + //% blockCombine block="parent" callInDebugger + public get parent() { + return this.parent_ + } + public set parent(p: Affine) { + this.parent_ = p + } + + //% blockCombine block="root" callInDebugger + public get root() { + let node = this.parent + while (node && node.parent) { + node = node.parent + } + return node + } + + constructor() { + this.localPos_ = new Vec2() + } + + public copyFrom(src: Affine): this { + this.localPos.copyFrom(src.localPos) + return this + } + + public clone(): Affine { + const aff = new Affine() + aff.copyFrom(this) + return aff + } + + private computeWorldPos(): Vec2 { + const pos = new Vec2() + pos.copyFrom(this.localPos_) + let parent = this.parent_ + while (parent) { + Vec2.TranslateToRef(pos, parent.localPos, pos) + parent = parent.parent + } + return pos + } + + public transformToRef(v: Vec2, ref: Vec2): Vec2 { + return Vec2.TranslateToRef(v, this.worldPos, ref) + } + } +} diff --git a/libs/microcode/analytics.ts b/libs/microcode/analytics.ts new file mode 100644 index 00000000000..a6dd42a8a73 --- /dev/null +++ b/libs/microcode/analytics.ts @@ -0,0 +1,33 @@ +namespace microcode { + export interface AnalyticsEvent { + type: "event" + msg: string + data?: { [name: string]: string | number } + } + + /** + * Sends an event to analytics. + */ + //% shim=TD_NOOP + export function reportEvent( + event: string, + data?: { [name: string]: string | number } + ) { + const msg: AnalyticsEvent = { + type: "event", + msg: event, + } + if (data) msg.data = data + report(msg) + } + + /** + * Sends an analytics message + * @param msg + */ + //% shim=TD_NOOP + function report(msg: AnalyticsEvent) { + const buf = Buffer.fromUTF8(JSON.stringify(msg)) + control.simmessages.send("analytics", buf) + } +} diff --git a/libs/microcode/app.ts b/libs/microcode/app.ts new file mode 100644 index 00000000000..0bf7692cf32 --- /dev/null +++ b/libs/microcode/app.ts @@ -0,0 +1,55 @@ +namespace microcode { + // Auto-save slot + export const SAVESLOT_AUTO = "sa" + + export interface SavedState { + progdef: any + version?: string + } + + export class App { + sceneManager: SceneManager + + constructor() { + // One interval delay to ensure all static constructors have executed. + setTimeout(() => { + reportEvent("app.start") + + this.sceneManager = new SceneManager() + const home = new Home(this) + this.pushScene(home) + }, 1) + } + + public saveBuffer(slot: string, buf: Buffer) { + reportEvent("app.save", { slot: slot, size: buf.length }) + console.log(`save to ${slot}: ${buf.length}b`) + profile() + settings.writeBuffer(slot, buf) + } + + public save(slot: string, prog: ProgramDefn) { + this.saveBuffer(slot, prog.toBuffer()) + } + + public load(slot: string): ProgramDefn { + try { + let buf = settings.readBuffer(slot) + if (buf) { + return ProgramDefn.fromBuffer(new BufferReader(buf)) + } + } catch (e) { + console.log(e) + } + return undefined + } + + public pushScene(scene: Scene) { + this.sceneManager.pushScene(scene) + } + + public popScene() { + this.sceneManager.popScene() + } + } +} diff --git a/libs/microcode/assets.ts b/libs/microcode/assets.ts new file mode 100644 index 00000000000..fce9f47bb5b --- /dev/null +++ b/libs/microcode/assets.ts @@ -0,0 +1,3512 @@ +namespace microcode { + let extraImage: SImage = null + + //% shim=TD_NOOP + function extraSamples(name: string) { + if (name == "clap_lights") extraImage = icondb.sampleClapLights + if (name == "firefly") extraImage = icondb.sampleFirefly + if (name == "flashing_heart") extraImage = icondb.sampleFlashingHeart + // if (name == "dice") extraImage = icondb.sampleDice // nice icon, don't delete, but not currently used + if (name == "rock_paper_scissors") + extraImage = icondb.sampleRockPaperScissors + if (name == "teleport_duck") extraImage = icondb.sampleTeleportDuck + if (name == "pet_hamster") extraImage = icondb.samplePetHamster + if (name == "heads_tails") extraImage = icondb.sampleHeadsOrTails + if (name == "reaction_time") extraImage = icondb.sampleReactionTime + if (name == "hot_potato") extraImage = icondb.sampleHotPotato + if (name == "clap_lights") extraImage = icondb.sampleClapLights + if (name == "railroad_crossing") + extraImage = icondb.sampleRailCrossingLight + } + + function carImages(name: string) { + if (name == TID_ACTUATOR_CAR) return icondb.car + if (name == TID_MODIFIER_CAR_FORWARD) return icondb.car_forward + if (name == TID_MODIFIER_CAR_REVERSE) return icondb.car_reverse + if (name == TID_MODIFIER_CAR_TURN_LEFT) return icondb.car_left_turn + if (name == TID_MODIFIER_CAR_TURN_RIGHT) return icondb.car_right_turn + if (name == TID_MODIFIER_CAR_STOP) return icondb.car_stop + if (name == TID_MODIFIER_CAR_FORWARD_FAST) + return icondb.car_forward_fast + if (name == TID_MODIFIER_CAR_SPIN_LEFT) return icondb.car_left_spin + if (name == TID_MODIFIER_CAR_SPIN_RIGHT) return icondb.car_right_spin + if (name == TID_MODIFIER_CAR_LED_COLOR_1) return icondb.tile_color_red + if (name == TID_MODIFIER_CAR_LED_COLOR_2) return icondb.tile_color_green + if (name == TID_MODIFIER_CAR_LED_COLOR_3) return icondb.tile_color_blue + if (name == TID_MODIFIER_CAR_LED_COLOR_4) return icondb.tile_color_black + if (name == TID_MODIFIER_CAR_ARM_OPEN) return icondb.arm_open + if (name == TID_MODIFIER_CAR_ARM_CLOSE) return icondb.arm_close + if (name == TID_SENSOR_CAR_WALL) return icondb.car_wall + if (name == TID_SENSOR_LINE) return icondb.line_sensor + if (name == TID_FILTER_LINE_LEFT) return icondb.line_left_on + if (name == TID_FILTER_LINE_RIGHT) return icondb.line_right_on + if (name == TID_FILTER_LINE_BOTH) return icondb.line_both_on + if (name == TID_FILTER_LINE_NEITHER) return icondb.line_neither_on + if (name == TID_FILTER_LINE_NEITHER_LEFT) + return icondb.line_none_from_left + if (name == TID_FILTER_LINE_NEITHER_RIGHT) + return icondb.line_none_from_right + return null + } + + // TODO: factor out all the jacdac stuff into separate file/class + // TODO: so we can generate different builds + function jacdacImages(name: string) { + if (name == TID_FILTER_KITA_KEY_1) return icondb.kita_key_1 + if (name == TID_FILTER_KITA_KEY_2) return icondb.kita_key_2 + if (name == TID_SENSOR_MAGNET) return icondb.magnet + if (name == TID_SENSOR_SLIDER) return icondb.kita_slider + if (name == TID_SENSOR_ROTARY) return icondb.kita_rotary + if (name == TID_FILTER_ROTARY_LEFT) return icondb.kita_rotary_left + if (name == TID_FILTER_ROTARY_RIGHT) return icondb.kita_rotary_right + if (name == TID_ACTUATOR_RGB_LED) return icondb.rgbLed + if (name == TID_MODIFIER_RGB_LED_COLOR_1) return icondb.tile_color_red + if (name == TID_MODIFIER_RGB_LED_COLOR_2) return icondb.tile_color_green + if (name == TID_MODIFIER_RGB_LED_COLOR_3) return icondb.tile_color_blue + if (name == TID_MODIFIER_RGB_LED_COLOR_4) + return icondb.tile_color_magenta + if (name == TID_MODIFIER_RGB_LED_COLOR_5) + return icondb.tile_color_yellow + if (name == TID_MODIFIER_RGB_LED_COLOR_6) return icondb.tile_color_black + if (name == TID_MODIFIER_RGB_LED_COLOR_RAINBOW) + return icondb.tile_rainbow + if (name == TID_MODIFIER_RGB_LED_COLOR_SPARKLE) + return icondb.tile_sparkle + if (name == TID_ACTUATOR_SERVO_SET_ANGLE) return icondb.servo_set_angle + if (name == TID_SENSOR_LIGHT) return icondb.light_sensor + return null + } + + export class icons { + public static get(name: string, nullIfMissing = false): SImage { + // editor icons + if (name == "delete") return icondb.btn_delete + if (name == "plus") return icondb.btn_plus + if (name == "arith_plus") return icondb.arith_plus + if (name == "arith_equals") return icondb.arith_equals + if (name == "when_insertion_point") + return icondb.btn_when_insertion_point + if (name == "do_insertion_point") + return icondb.btn_do_insertion_point + if (name == "rule_arrow") return icondb.rule_arrow + if (name == "rule_handle") return icondb.rule_handle + if (name == "edit_program") return icondb.largeEditIcon + if (name == "new_program") return icondb.largeNewProgramIcon + if (name == "MISSING") return icondb.MISSING + if (name == "disk") return icondb.disk + if (name == "disk1") return icondb.disk1 + if (name == "disk2") return icondb.disk2 + if (name == "disk3") return icondb.disk3 + if (name == "largeDisk") return icondb.largeDiskIcon + + // basic colors led editor + if (name == "solid_red") return icondb.solid_red + if (name == "solid_black") return icondb.solid_black + if (name == "note_on") return icondb.note_on + if (name == "note_off") return icondb.note_off + + // sample icons + if (name == "smiley_buttons") return icondb.sampleSmileyButtons + + // pages + + if (name == TID_SENSOR_START_PAGE) return icondb.tile_start_page + if (name == TID_ACTUATOR_SWITCH_PAGE) return icondb.tile_switch_page + if (name == TID_MODIFIER_PAGE_1) return icondb.tile_page_1 + if (name == TID_MODIFIER_PAGE_2) return icondb.tile_page_2 + if (name == TID_MODIFIER_PAGE_3) return icondb.tile_page_3 + if (name == TID_MODIFIER_PAGE_4) return icondb.tile_page_4 + if (name == TID_MODIFIER_PAGE_5) return icondb.tile_page_5 + + // looping + if (name == TID_MODIFIER_LOOP) return icondb.loop + + // variables + + if (name == TID_SENSOR_CUP_X_WRITTEN) return icondb.cupXwritten + if (name == TID_SENSOR_CUP_Y_WRITTEN) return icondb.cupYwritten + if (name == TID_SENSOR_CUP_Z_WRITTEN) return icondb.cupZwritten + if (name == TID_FILTER_CUP_X_READ) return icondb.cupXread + if (name == TID_FILTER_CUP_Y_READ) return icondb.cupYread + if (name == TID_FILTER_CUP_Z_READ) return icondb.cupZread + if (name == TID_ACTUATOR_CUP_X_ASSIGN) return icondb.cupXassign + if (name == TID_ACTUATOR_CUP_Y_ASSIGN) return icondb.cupYassign + if (name == TID_ACTUATOR_CUP_Z_ASSIGN) return icondb.cupZassign + if (name == TID_MODIFIER_CUP_X_READ) return icondb.cupXread + if (name == TID_MODIFIER_CUP_Y_READ) return icondb.cupYread + if (name == TID_MODIFIER_CUP_Z_READ) return icondb.cupZread + + // numbers + if (name == TID_MODIFIER_RANDOM_TOSS) return icondb.diceToss + if (name == TID_FILTER_COIN_1) return icondb.blocks1 + if (name == TID_FILTER_COIN_2) return icondb.blocks2 + if (name == TID_FILTER_COIN_3) return icondb.blocks3 + if (name == TID_FILTER_COIN_4) return icondb.blocks4 + if (name == TID_FILTER_COIN_5) return icondb.blocks5 + if (name == TID_MODIFIER_COIN_1) return icondb.blocks1 + if (name == TID_MODIFIER_COIN_2) return icondb.blocks2 + if (name == TID_MODIFIER_COIN_3) return icondb.blocks3 + if (name == TID_MODIFIER_COIN_4) return icondb.blocks4 + if (name == TID_MODIFIER_COIN_5) return icondb.blocks5 + + // micro:bit sensors + if (name == TID_SENSOR_ACCELEROMETER) return icondb.accelerometer + if (name == TID_SENSOR_TIMER) return icondb.tile_timer + if (name == TID_SENSOR_RADIO_RECEIVE) return icondb.radio_receive + if (name == TID_SENSOR_PRESS) return icondb.finger_press + if (name == TID_SENSOR_RELEASE) return icondb.finger_release + if (name == TID_SENSOR_MICROPHONE) return icondb.microphone + if (name == TID_SENSOR_TEMP) return icondb.thermometer + if (name == TID_SENSOR_LED_LIGHT) return icondb.led_light_sensor + + // micro:bit filters + if (name == TID_FILTER_LOGO) return icondb.microbit_logo + if (name == TID_FILTER_PIN_0) return icondb.tile_pin_0 + if (name == TID_FILTER_PIN_1) return icondb.tile_pin_1 + if (name == TID_FILTER_PIN_2) return icondb.tile_pin_2 + if (name == TID_FILTER_BUTTON_A) return icondb.tile_button_a + if (name == TID_FILTER_BUTTON_B) return icondb.tile_button_b + if (name == TID_FILTER_TIMESPAN_SHORT) + return icondb.tile_timespan_short + if (name == TID_FILTER_TIMESPAN_LONG) + return icondb.tile_timespan_long + if (name == TID_FILTER_TIMESPAN_VERY_LONG) + return icondb.tile_timespan_fiveSeconds + if (name == TID_FILTER_TIMESPAN_RANDOM) + return icondb.tile_timespan_random + if (name == TID_FILTER_LOUD) return icondb.speaker + if (name == TID_FILTER_TEMP_WARMER) return icondb.temp_warmer + if (name == TID_FILTER_TEMP_COLDER) return icondb.temp_colder + if (name == TID_FILTER_ACCEL_SHAKE) return icondb.moveShake + if (name == TID_FILTER_ACCEL_TILT_UP) return icondb.moveTiltUp + if (name == TID_FILTER_ACCEL_TILT_DOWN) return icondb.moveTiltDown + if (name == TID_FILTER_ACCEL_TILT_LEFT) return icondb.moveTiltLeft + if (name == TID_FILTER_ACCEL_TILT_RIGHT) return icondb.moveTiltRight + if (name == TID_FILTER_ACCEL_FACE_UP) return icondb.moveFaceUp + if (name == TID_FILTER_ACCEL_FACE_DOWN) return icondb.moveFaceDown + + // micro:bit actuators + if (name == TID_ACTUATOR_PAINT) return icondb.showScreen + if (name == TID_ACTUATOR_SHOW_NUMBER) return icondb.showNumber + if (name == TID_ACTUATOR_RADIO_SEND) return icondb.radio_send + if (name == TID_ACTUATOR_RADIO_SET_GROUP) + return icondb.radio_set_group + if (name == TID_ACTUATOR_SPEAKER) return icondb.speakerFun + if (name == TID_ACTUATOR_MUSIC) return icondb.music + + // micro:bit modifiers + if (name == TID_MODIFIER_ICON_EDITOR) return icondb.iconEditor + if (name == TID_MODIFIER_MELODY_EDITOR) return icondb.melodyEditor + + if (name == TID_MODIFIER_EMOJI_GIGGLE) return icondb.soundGiggle + if (name == TID_MODIFIER_EMOJI_HAPPY) return icondb.soundHappy + if (name == TID_MODIFIER_EMOJI_HELLO) return icondb.soundHello + if (name == TID_MODIFIER_EMOJI_MYSTERIOUS) + return icondb.soundMysterious + if (name == TID_MODIFIER_EMOJI_SAD) return icondb.soundSad + if (name == TID_MODIFIER_EMOJI_SLIDE) return icondb.soundSlide + if (name == TID_MODIFIER_EMOJI_SOARING) return icondb.soundSoaring + if (name == TID_MODIFIER_EMOJI_SPRING) return icondb.soundSpring + if (name == TID_MODIFIER_EMOJI_TWINKLE) return icondb.soundTwinkle + if (name == TID_MODIFIER_EMOJI_YAWN) return icondb.soundYawn + + if (name == TID_MODIFIER_TEMP_READ) return icondb.thermometer + if (name == TID_MODIFIER_RADIO_VALUE) return icondb.radio_value + + // micro:bit car + const car = carImages(name) + if (car) return car + const jacdac = jacdacImages(name) + if (jacdac) return jacdac + extraImage = null + extraSamples(name) // only for web app + if (extraImage) return extraImage + if (nullIfMissing) return null + return icondb.MISSING + } + } + + export const wordLogo = img` + .111111.......111111...1111.......................................................1111111.................................1111.................. + 11bbbbbb.....11bbbbbb.11bbbb....................................................111bbbbbbb1..............................11bbbb................. + 1bbbbbbbb...11bbbbbbbf1bbbbbf..................................................11bbbbbbbbbbb.............................1bbbbbf................ + 1bbbbbbbbb.11bbbbbbbbf1bbbbbf.................................................11bbbbbbbbbbbbb............................1bbbbbf................ + 1bbbbbbbbbb1bbbbbbbbbf1bbbbbf................................................11bbbbbbbbbbbbbbb...........................1bbbbbf................ + 1bbbbbbbbbbbbbbbbbbbbf.bbbbff...............................................11bbbbbbbbbbbbbbbbf..........................1bbbbbf................ + 1bbbbbbbbbbbbbbbbbbbbf..ffff.....1111111......1111...111.......1111111......1bbbbbbbbbbbbbbbbbb.....1111111.........111111bbbbbf....1111111..... + 1bbbbbbbbbbbbbbbbbbbbf.1111....111bbbbbbb1...11bbbb.11bbb....111bbbbbbb1...11bbbbbbbfffbbbbbbbbf..111bbbbbbb1.....111bbbbbbbbbbf..111bbbbbbb1... + 1bbbbbbbbbbbbbbbbbbbbf11bbbb..11bbbbbbbbbbb..1bbbbbb1bbbbb..11bbbbbbbbbbb..1bbbbbbbff...bbbbbbbf.11bbbbbbbbbbb...11bbbbbbbbbbbbf.11bbbbbbbbbbb.. + 1bbbbbbfbbbbbfbbbbbbbf1bbbbbf.1bbbbbbbbbbbbf.1bbbbbbbbbbbbf.1bbbbbbbbbbbbf.1bbbbbbff.....bbbbbff.1bbbbbbbbbbbbf..1bbbbbbbbbbbbbf.1bbbbbbbbbbbbf. + 1bbbbbbf.bbbff1bbbbbbf1bbbbbf11bbbbbbbbbbbbb.1bbbbbbbbbbbbf11bbbbbbbbbbbbb.1bbbbbbf.......fffff.11bbbbbbbbbbbbb.11bbbbbbbbbbbbbf11bbbbfffbbbbbb. + 1bbbbbbf..fff.1bbbbbbf1bbbbbf1bbbbbfffbbbbbbf1bbbbbfffbbbff1bbbbbfffbbbbbbf1bbbbbbf......11111..1bbbbbfffbbbbbbf1bbbbbfffbbbbbbf1bbbbff...bbbbbf + 1bbbbbbf......1bbbbbbf1bbbbbf1bbbbff...bbbbff1bbbbbf...fff.1bbbbff...bbbbbf1bbbbbbb.....11bbbbb.1bbbbff...bbbbbf1bbbbff..1bbbbbf1bbbbb11111bbbbf + 1bbbbbbf......1bbbbbbf1bbbbbf1bbbbf.....ffff.1bbbbbf.......1bbbbf....1bbbbf1bbbbbbbb...11bbbbbbf1bbbbf....1bbbbf1bbbbf...1bbbbbf1bbbbbbbbbbbbbbf + 1bbbbbbf......1bbbbbbf1bbbbbf1bbbbf....1111..1bbbbbf.......1bbbbf....1bbbbf.bbbbbbbbb111bbbbbbbf1bbbbf....1bbbbf1bbbbf...1bbbbbf1bbbbbbbbbbbbbff + 1bbbbbbf......1bbbbbbf1bbbbbf1bbbbb...11bbbb.1bbbbbf.......1bbbbb...11bbbbf.1bbbbbbbbbbbbbbbbbff1bbbbb...11bbbbf1bbbbb...1bbbbbf1bbbbffffffffff. + 1bbbbbbf......1bbbbbbf1bbbbbf1bbbbbb111bbbbbf1bbbbbf.......1bbbbbb111bbbbbf..bbbbbbbbbbbbbbbbbf.1bbbbbb111bbbbbf1bbbbbb111bbbbbf1bbbbb.......... + 1bbbbbbf......1bbbbbbf1bbbbbf.bbbbbbbbbbbbbff1bbbbbf........bbbbbbbbbbbbbff...bbbbbbbbbbbbbbbff..bbbbbbbbbbbbbff.bbbbbbbbbbbbbbf.bbbbbb11111.... + 1bbbbbbf......1bbbbbbf1bbbbbf.1bbbbbbbbbbbbf.1bbbbbf........1bbbbbbbbbbbbf.....bbbbbbbbbbbbbff...1bbbbbbbbbbbbf..1bbbbbbbbbbbbbf.1bbbbbbbbbbb... + 1bbbbbbf......1bbbbbbf1bbbbbf..bbbbbbbbbbbff.1bbbbbf.........bbbbbbbbbbbff......bbbbbbbbbbbff.....bbbbbbbbbbbff...bbbbbbbbbbbbbf..bbbbbbbbbbbf.. + .bbbbbff.......bbbbbff.bbbbff...fbbbbbbbfff...bbbbff..........fbbbbbbbfff........fbbbbbbbfff.......fbbbbbbbfff.....fbbbbbbbbbbff...fbbbbbbbbff.. + ..fffff.........fffff...ffff......fffffff......ffff.............fffffff............fffffff...........fffffff.........ffffffffff......ffffffff... + ` + export const microbitLogo = img` + ............................ + ......5555555555555555...... + ....55555555555555555555.... + ...5554444444444444444555... + ..5554.................555.. + ..554...................554. + .554....55........55.....554 + .55....5555......5555....554 + .55....55554.....55554...554 + .55.....5544......5544...554 + ..55.....44........44...5544 + ..555..................5554. + ...555................55544. + ....5555555555555555555544.. + .....45555555555555555444... + .......4444444444444444..... + ` + + export const editorBackground = img` + 8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 + 8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 + 8888888888888888888888188888881888888888881888888888888888888888888888888888888888888888888888888888885888888888888888888881888888888888888888818888888888888188 + 8818888888881888888888888888818188888888888888888818888888881888888888818888888888881888888888588888888888888888888888888888888888888858888888888888888888888888 + 8888888888888888888888888888881888888888888888888181888888888888888888888888888888888888888888888881888881888888888188888888888888888888888888888888888888888888 + 8888588888888818888888888888888888888888888888888818888888888888888888888888888888888888888888888888888888888888881818888888888881888888818888888888818888888888 + 8888888888888888888858888888888888188888888888888888888888888888818888888888888188888888888888888888888888888888888188888888888888888888888888888888888888888888 + 8888888888888888188888888888888888888881888885888888888888888888888888885888888888888888881888888588888888888888888888888888888888888888888888888588888888888888 + 8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 + 8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 + 8886888688868886888688868886888688868886888688868886888688868886888688868886888688868886888688868886888688868886888688868886888688868886888688868886888688868886 + 8686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686868686 + 6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 + 6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 + 6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 + 6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666 + ` +} + +namespace icondb { + const note4x3 = img` + . f f . + f c c . + f c c . +` + export function melodyToImage(melody: microcode.Melody) { + const ret = image.create(16, 16) + ret.fill(1) + for (let col = 0; col < microcode.MELODY_LENGTH; col++) { + if (melody.notes[col] === ".") continue + const row = microcode.NUM_NOTES - 1 - parseInt(melody.notes[col]) + const color = 15 + const ncol = col << 2, + nrow = row * 3 + 1 + ret.drawTransparentImage(note4x3, ncol, nrow) + } + return ret + } + + // - upscale 5x5 image to 16 x 16, add halo + export function renderMicrobitLEDs(led55: SImage) { + const ret = image.create(16, 16) + ret.fill(15) + for (let row = 0; row < 5; row++) { + for (let col = 0; col < 5; col++) { + const on = led55.getPixel(row, col) + if (!on) continue + + const color = 0x2 + const halo = 0xe + const nrow = 1 + row * 3, + ncol = 1 + col * 3 + ret.setPixel(nrow, ncol, color) + ret.setPixel(nrow + 1, ncol, color) + ret.setPixel(nrow, ncol + 1, color) + ret.setPixel(nrow + 1, ncol + 1, color) + // halo + /* + ret.setPixel(nrow - 1, ncol, halo) + ret.setPixel(nrow - 1, ncol + 1, halo) + ret.setPixel(nrow + 2, ncol, halo) + ret.setPixel(nrow + 2, ncol + 1, halo) + ret.setPixel(nrow, ncol - 1, halo) + ret.setPixel(nrow + 1, ncol - 1, halo) + ret.setPixel(nrow, ncol + 2, halo) + ret.setPixel(nrow + 1, ncol + 2, halo) + */ + } + } + return ret + } + + /* + export const iconEditor = renderMicrobitLEDs( + img` + . . . . . + . 1 . 1 . + . . . . . + 1 . . . 1 + . 1 1 1 . + ` + ) + */ + export const iconEditor = img` + f f f f f f f f f f f f f f f f + f f f f f f f f f f f f f f f f + f f f f f f f f f f f f f f f f + f f f f e e f f f f e e f f f f + f f f e 2 2 e f f e 2 2 e f f f + f f f e 2 2 e f f e 2 2 e f f f + f f f f e e f f f f e e f f f f + f f f f f f f f f f f f f f f f + f f f f f f f f f f f f f f f f + f e e f f f f f f f f f f e e f + e 2 2 e f f f f f f f f e 2 2 e + e 2 2 e f f f f f f f f e 2 2 e + f e e f e e f e e f e e f e e f + f f f e 2 2 e 2 2 e 2 2 e f f f + f f f e 2 2 e 2 2 e 2 2 e f f f + f f f f e e f e e f e e f f f f + ` + function renderImg(i: SImage) { + let r = "" + for (let y = 0; y < i.height; ++y) { + let line = "" + for (let x = 0; x < i.width; ++x) + line += "0123456789abcdef"[i.getPixel(x, y)] + " " + r += line + "\n" + } + console.log(`\nimg\`\n${r}\``) + } + + /* + export const melodyEditor = melodyToImage({ + notes: "0240", + tempo: 0, + }) + */ + export const melodyEditor = img` + 1111111111111111 + 111111111ff11111 + 11111111fcc11111 + 11111111fcc11111 + 1111111111111111 + 1111111111111111 + 1111111111111111 + 11111ff111111111 + 1111fcc111111111 + 1111fcc111111111 + 1111111111111111 + 1111111111111111 + 1111111111111111 + 1ff1111111111ff1 + fcc111111111fcc1 + fcc111111111fcc1 + ` + + export const disk = img` + . . . . . . . . . . . . . . . . + . . 8 d d d d 8 8 d d 8 . . . . + . . 8 d d d d 8 8 d d 8 8 . . . + . . 8 d d d d 8 8 d d 8 8 8 . . + . . 8 d d d d d d d d 8 8 8 . . + . . 8 8 8 8 8 8 8 8 8 8 8 8 . . + . . 8 8 3 3 3 3 3 3 3 3 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const disk1 = img` + . . . . . . . . . . . . . . . . + . . 8 d d d d 8 8 d d 8 . . . . + . . 8 d d d d 8 8 d d 8 8 . . . + . . 8 d d d d 8 8 d d 8 8 8 . . + . . 8 d d d d d d d d 8 8 8 . . + . . 8 8 8 8 8 8 8 8 8 8 8 8 d . + . . 8 8 3 3 3 3 3 3 3 3 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 d . + . . 8 8 1 1 1 1 f 1 1 1 8 8 d . + . . 8 8 1 1 1 f f 1 1 1 8 8 d . + . . 8 8 1 1 1 1 f 1 1 1 8 8 d . + . . 8 8 1 1 1 1 f 1 1 1 8 8 d . + . . 8 8 1 1 1 f f f 1 1 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const disk2 = img` + . . . . . . . . . . . . . . . . + . . 8 d d d d 8 8 d d 8 . . . . + . . 8 d d d d 8 8 d d 8 8 . . . + . . 8 d d d d 8 8 d d 8 8 8 . . + . . 8 d d d d d d d d 8 8 8 . . + . . 8 8 8 8 8 8 8 8 8 8 8 8 d . + . . 8 8 3 3 3 3 3 3 3 3 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 d . + . . 8 8 1 1 1 f f 1 1 1 8 8 d . + . . 8 8 1 1 1 1 1 f 1 1 8 8 d . + . . 8 8 1 1 1 1 f 1 1 1 8 8 d . + . . 8 8 1 1 1 f 1 1 1 1 8 8 d . + . . 8 8 1 1 1 f f f 1 1 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const disk3 = img` + . . . . . . . . . . . . . . . . + . . 8 d d d d 8 8 d d 8 . . . . + . . 8 d d d d 8 8 d d 8 8 . . . + . . 8 d d d d 8 8 d d 8 8 8 . . + . . 8 d d d d d d d d 8 8 8 . . + . . 8 8 8 8 8 8 8 8 8 8 8 8 d . + . . 8 8 3 3 3 3 3 3 3 3 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 d . + . . 8 8 1 1 1 f f 1 1 1 8 8 d . + . . 8 8 1 1 1 1 1 f 1 1 8 8 d . + . . 8 8 1 1 1 1 f f 1 1 8 8 d . + . . 8 8 1 1 1 1 1 f 1 1 8 8 d . + . . 8 8 1 1 1 f f 1 1 1 8 8 d . + . . 8 8 1 1 1 1 1 1 1 1 8 8 . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const largeDiskIcon = img` + .666666666666666666666666666666. + 66666666666666666666666666666666 + 66666666666666666666666666666666 + 66666bbbbbbbbbbbbbbbbbbbb6666666 + 6666bb8cdddddddddddd888c8b666666 + 6666b88cdddddddc88dd888c88b66666 + 6666b88cddddddd888dd888c888b6666 + 6666b88cddddddd888dd888c888b6666 + 6666b88cddddddd888dd888c888b6666 + 6666b88cddddddd888dd888c888b6666 + 6666b88cdddddddddddd888c888b6666 + 6666b88ccccccccccccccccc888b6666 + 6666b8888888888888888888888b6666 + 6666b8888888888888888888888b6666 + 6666b8833333333333333333888b6666 + 6666b8833333333333333333888b6666 + 6666b8811111111111111111888b6666 + 6666b8811111111111111111888b6666 + 6666b8811ccccc1111111111888b6666 + 6666b8811111111111111111888b6666 + 6666b8811ccc111111111111888b6666 + 6666b8811111111111111111888b6666 + 6666b8811ccccccc11111111888b6666 + 6666b8811111111111111111888b6666 + 6666b8811111111111111111888b6666 + 6666b88111111111111111118f8b6666 + 6666b88111111111111111118f8b6666 + 6666bb811111111111111111888b6666 + 66666bbbbbbbbbbbbbbbbbbbbbbb6666 + 66666666666666666666666666666666 + 66666666666666666666666666666666 + b666666666666666666666666666666b + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + + export const MISSING = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . 2 2 2 2 2 2 2 2 2 2 . . . + . . . 2 2 . . . . . . 2 2 . . . + . . . 2 . 2 . . . . 2 . 2 . . . + . . . 2 . . 2 . . 2 . . 2 . . . + . . . 2 . . . 2 2 . . . 2 . . . + . . . 2 . . . 2 2 . . . 2 . . . + . . . 2 . . 2 . . 2 . . 2 . . . + . . . 2 . 2 . . . . 2 . 2 . . . + . . . 2 2 . . . . . . 2 2 . . . + . . . 2 2 2 2 2 2 2 2 2 2 . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + export const solid_red = img` + . . . . . . . . . . . . . . . . + . . . 2 2 2 2 2 2 2 2 2 2 . . . + . . 2 2 4 4 4 4 4 4 4 4 2 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 4 4 4 4 4 4 4 4 4 4 2 . . + . . 2 2 4 4 4 4 4 4 4 4 2 2 . . + . . . 2 2 2 2 2 2 2 2 2 2 . . . + . . . . . . . . . . . . . . . . +` + + export const solid_black = img` + . . . . . . . . . . . . . . . . + . . . c c c c c c c c c c . . . + . . c c f f f f f f f f c c . . + . . c f f f f f f f f f f c . . + . . c f f f f f f f f f f c . . + . . c f f f f f f f f f f c . . + . . c f f f f f f f f f f c . . + . . c f f f f f f f f f f c . . + . . c f f f f f f f f f f c . . + . . c f f f f f f f f f f c . . + . . c f f f f f f f f f f c . . + . . c f f f f f f f f f f c . . + . . c f f f f f f f f f f c . . + . . c c f f f f f f f f c c . . + . . . c c c c c c c c c c . . . + . . . . . . . . . . . . . . . . +` + + export const arm_open = img` + . . . . . . . . . . c c c . . . + . . . . . . . . c c b b b c c . + . . . . . . . c b b b b b b b c + . . . . . . c b b b b c c b b c + c c . . c b b b b c c . . c b c + b b c c b b b b c . . . . . c . + b b b b b b c c . . . . . . . . + c f f b b c . . . . . . . . . . + c f f b b c . . . . . . . . . . + b b b b b b c c . . . . . . . . + b b c c b b b b c . . . . . c . + c c . . c b b b b c c . . c b c + . . . . . c c b b b b c c b b c + . . . . . . . c b b b b b b b c + . . . . . . . . c c b b b c c . + . . . . . . . . . . c c c . . . + ` + + export const arm_close = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . c c c c c c c c c . . + . . . . . c b b b b b b b b c . + . . . . c b b b b b b b b b b c + c c d d b b c c c c c c c c b c + b b b b b b c . . . . . . . c . + c f f b b c . . . . . . . . . . + c f f b b c . . . . . . . . . . + b b b b b b c . . . . . . . c . + c c d d b b c c c c c c c c b c + . . . . c b b b b b b b b b b c + . . . . . c b b b b b b b b c . + . . . . . c c c c c c c c c . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . ` + + /// + /// BUTTON ICONS + /// + export const btn_stop = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . f f f f f f f f . . . . + . . . . f 2 2 2 2 2 2 f . . . . + . . . . f 2 2 2 2 2 2 f . . . . + . . . . f 2 2 2 2 2 2 f . . . . + . . . . f 2 2 2 2 2 2 f . . . . + . . . . f 2 2 2 2 2 2 f . . . . + . . . . f 2 2 2 2 2 2 f . . . . + . . . . f f f f f f f f . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + export const btn_delete = img` + . . . . . . . . . . . . . . . . + . . . . . . c f f . . . . . . . + . . . . . c . . . f . . . . . . + . . . . c c c f f f f . . . . . + . . . c 1 1 d d d b b f . . . . + . . c c c c c f f f f f f . . . + . . . c b c b c b c c f . . . . + . . . c 1 c d c d c b f d . . . + . . . c 1 c d c d c b f d . . . + . . . c 1 c d c d c b f d . . . + . . . c 1 c d c d c b f d . . . + . . . c 1 1 d d d b b f d . . . + . . . c 1 1 d d d b b f d . . . + . . . . c c c f f f f d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + export const btn_plus = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . f f f f . . . . . . + . . . . . . f 5 5 f . . . . . . + . . . . . . f 5 5 f . . . . . . + . . . f f f f 5 5 f f f f . . . + . . . f 5 5 5 5 5 5 5 5 f . . . + . . . f 5 5 5 5 5 5 5 5 f . . . + . . . f f f f 5 5 f f f f . . . + . . . . . . f 5 5 f . . . . . . + . . . . . . f 5 5 f . . . . . . + . . . . . . f f f f . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + export const arith_plus = img` + . . . . . . . . + . . . f f . . . + . . . f f . . . + . f f f f f f . + . f f f f f f . + . . . f f . . . + . . . f f . . . + . . . . . . . . +` + + export const arith_equals = img` + . . . . . . . . + . f f f f f f . + . f f f f f f . + . . . . . . . . + . . . . . . . . + . f f f f f f . + . f f f f f f . + . . . . . . . . + +` + + export const loop = img` + 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 + 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 + 4 4 4 f f f f 4 4 4 4 4 4 4 4 4 + 4 4 4 c c c c f 4 4 4 4 4 4 4 4 + 4 4 4 1 1 1 1 1 f 4 4 4 4 4 4 4 + 4 4 4 4 4 4 c 1 1 4 1 4 4 4 1 4 + 4 4 4 4 4 4 4 c 1 4 4 1 4 1 4 4 + 4 4 4 4 f 4 4 c 1 4 4 4 1 4 4 4 + 4 4 4 f c 4 f c 1 4 4 1 4 1 4 4 + 4 4 f c 1 f c 1 1 4 1 4 4 4 1 4 + 4 4 c 1 1 c 1 1 4 4 4 4 4 4 4 4 + 4 4 1 1 1 1 1 4 4 4 4 4 4 4 4 4 + 4 4 4 1 1 4 4 4 4 4 4 4 4 4 4 4 + 4 4 4 4 1 4 4 4 4 4 4 4 4 4 4 4 + 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 + 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 + ` + + export const btn_when_insertion_point = img` + dddddddddddddddddd + dcddcddcddcddcddcd + dddddddddddddddddd + dddddddddddddddddd + dcddddddddddddddcd + dddddddddddddddddd + dddddddddddddddddd + dcddddddddddddddcd + dddddddddddddddddd + dddddddddddddddddd + dcddddddddddddddcd + dddddddddddddddddd + dddddddddddddddddd + dcddddddddddddddcd + dddddddddddddddddd + dddddddddddddddddd + dcddcddcddcddcddcd + dddddddddddddddddd + ` + + export const btn_do_insertion_point = img` + bbbbbbbbbbbbbbbbbb + bdbbdbbdbbdbbdbbdb + bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb + bdbbbbbbbbbbbbbbdb + bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb + bdbbbbbbbbbbbbbbdb + bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb + bdbbbbbbbbbbbbbbdb + bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb + bdbbbbbbbbbbbbbbdb + bbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbb + bdbbdbbdbbdbbdbbdb + bbbbbbbbbbbbbbbbbb + ` + + export const rule_arrow = img` + d d d . . . . . . . . . . . + d d d d . . . . . . . . . . + d d d d d . . . . . . . . . + d d d d d d . . . . . . . . + d d d d d d d . . . . . . . + d d d d d d d d . . . . . . + d d d d d d d d d . . . . . + d d d d d d d d d d . . . . + d d d d d d d d d d d . . . + d d d d d d d d d d d d . . + d d d d d d d d d d d d . . + d d d d d d d d d d d . . . + d d d d d d d d d d . . . . + d d d d d d d d d . . . . . + d d d d d d d d . . . . . . + d d d d d d d . . . . . . . + d d d d d d . . . . . . . . + d d d d d . . . . . . . . . + d d d d . . . . . . . . . . + d d d . . . . . . . . . . . +` + + export const showScreen = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . 2 4 . . + . . . . 2 . . . . . . 2 4 4 2 . + . . . . 2 . . . . . 2 4 4 2 e . + . 2 . . 2 . . . . 2 4 4 2 e b . + . . 2 . 2 . . . 2 4 4 2 e b . . + . . . . . . . d 4 4 2 e b . . . + . . f f f f f d d 2 e b . . . . + . . f f f f f 2 d d b . . . . . + . . f f 2 f 2 f f b . . . . . . + . . f f f f f f f b . 2 2 2 2 . + . . f 2 f f f 2 f b . . . . . . + . . f f 2 2 2 f f b . 2 . . . . + . . f f f f f f f b . . 2 . . . + . . . b b b b b b b . . . . . . + . . . . . . . . . . . . . . . . +` + + export const showNumber = img` + . . . . . . . . . . 4 4 4 4 4 4 + . . . . . . . . . 4 5 5 5 5 5 5 + . . . . 2 . . . 4 5 4 4 4 4 4 4 + . . . . 2 . . . 4 5 4 . . . . . + . 2 . . 2 . . . 4 5 4 . . . . . + . . 2 . 2 . 4 5 5 5 5 5 4 . . . + . . . . . . . 4 5 5 5 4 . . . . + . . f f f f f f 4 5 4 . . . . . + . . f f f 2 2 f f b . . . . . . + . . f f 2 f 2 f f b . . . . . . + . . f 2 f f 2 f f b . 2 2 2 2 . + . . f 2 2 2 2 2 f b . . . . . . + . . f f f f 2 f f b . 2 . . . . + . . f f f f f f f b . . 2 . . . + . . . b b b b b b b . . . . . . + . . . . . . . . . . . . . . . . + ` + + /// + /// GENERIC LANGUAGE TILES (NOT HARDWARE SPECIFIC) + /// + + export const rule_handle = img` + . f f f f f f f . + f 1 1 1 1 1 1 1 f + f 1 1 1 1 1 1 1 f + f 1 1 1 1 1 1 1 f + f 1 1 1 1 1 1 1 f + f 1 1 1 1 1 1 1 f + f 1 1 1 1 1 1 1 f + f 1 1 1 1 1 1 1 f + . f f f f f f f . + ` + + export const tile_switch_page = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . f f f f . . . . . . . + . . . . f f 9 9 f . . . . . . . + . . . f 9 f 9 9 f d f f f f . . + . . f f f f 9 9 f f f 7 7 f . . + . . f 9 9 9 9 9 f 7 f 7 7 f d . + . . f 9 9 9 9 f f f f 7 7 f d . + . . f 9 9 9 9 f 5 f 7 7 7 f d . + . . f 9 f f f f 5 5 f 7 7 f d . + . . f 9 f 5 5 5 5 5 5 f 7 f d . + . . f f f f f f 5 5 f 7 7 f d . + . . . d d d d f 5 f 7 7 7 f d . + . . . . . . . f f f f f f f d . + . . . . . . . . d d d d d d d . + . . . . . . . . . . . . . . . . + ` + + export const tile_start_page = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . f f f f . . . + . . . . . . . . f f 7 7 f . . . + . . . . . . . f 7 f 7 7 f d . . + . . . . . . f f f f 7 7 f d . . + . . . . . . f 5 f 7 7 7 f d . . + . . . f f f f 5 5 f 7 7 f d . . + . . . f 5 5 5 5 5 5 f 7 f d . . + . . . f f f f 5 5 f 7 7 f d . . + . . . d d d f 5 f 7 7 7 f d . . + . . . . . . f f f f f f f d . . + . . . . . . . d d d d d d d . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + export const tile_page_1 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . f f f f f f f . . . + . . . . . f f 9 9 9 9 9 f . . . + . . . . f 9 f 9 9 9 9 9 f . . . + . . . f f f f 9 9 9 9 9 f . . . + . . . f 9 9 9 9 9 9 9 9 f . . . + . . . f 9 9 9 f f 9 9 9 f . . . + . . . f 9 9 9 9 f 9 9 9 f . . . + . . . f 9 9 9 9 f 9 9 9 f . . . + . . . f 9 9 9 9 f 9 9 9 f . . . + . . . f 9 9 9 f f f 9 9 f . . . + . . . f 9 9 9 9 9 9 9 9 f . . . + . . . f f f f f f f f f f . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_page_2 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . f f f f f f f . . . + . . . . . f f 5 5 5 5 5 f . . . + . . . . f 5 f 5 5 5 5 5 f . . . + . . . f f f f 5 5 5 5 5 f . . . + . . . f 5 5 5 5 5 5 5 5 f . . . + . . . f 5 5 5 f f 5 5 5 f . . . + . . . f 5 5 f 5 5 f 5 5 f . . . + . . . f 5 5 5 5 f 5 5 5 f . . . + . . . f 5 5 5 f 5 5 5 5 f . . . + . . . f 5 5 f f f f 5 5 f . . . + . . . f 5 5 5 5 5 5 5 5 f . . . + . . . f f f f f f f f f f . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_page_3 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . f f f f f f f . . . + . . . . . f f 4 4 4 4 4 f . . . + . . . . f 4 f 4 4 4 4 4 f . . . + . . . f f f f 4 4 4 4 4 f . . . + . . . f 4 4 4 4 4 4 4 4 f . . . + . . . f 4 4 4 f f 4 4 4 f . . . + . . . f 4 4 4 4 4 f 4 4 f . . . + . . . f 4 4 4 4 f 4 4 4 f . . . + . . . f 4 4 4 4 4 f 4 4 f . . . + . . . f 4 4 4 f f 4 4 4 f . . . + . . . f 4 4 4 4 4 4 4 4 f . . . + . . . f f f f f f f f f f . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_page_4 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . f f f f f f f . . . + . . . . . f f 3 3 3 3 3 f . . . + . . . . f 3 f 3 3 3 3 3 f . . . + . . . f f f f 3 3 3 3 3 f . . . + . . . f 3 3 3 3 3 3 3 3 f . . . + . . . f 3 3 f 3 f 3 3 3 f . . . + . . . f 3 3 f 3 f 3 3 3 f . . . + . . . f 3 3 f f f f 3 3 f . . . + . . . f 3 3 3 3 f 3 3 3 f . . . + . . . f 3 3 3 3 f 3 3 3 f . . . + . . . f 3 3 3 3 3 3 3 3 f . . . + . . . f f f f f f f f f f . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_page_5 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . f f f f f f f . . . + . . . . . f f 7 7 7 7 7 f . . . + . . . . f 7 f 7 7 7 7 7 f . . . + . . . f f f f 7 7 7 7 7 f . . . + . . . f 7 7 7 7 7 7 7 7 f . . . + . . . f 7 7 f f f f 7 7 f . . . + . . . f 7 7 f 7 7 7 7 7 f . . . + . . . f 7 7 f f f 7 7 7 f . . . + . . . f 7 7 7 7 7 f 7 7 f . . . + . . . f 7 7 f f f 7 7 7 f . . . + . . . f 7 7 7 7 7 7 7 7 f . . . + . . . f f f f f f f f f f . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + /// + /// HARDWARE-SPECIFIC LANGUAGE TILES + /// + export const tile_button_a = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . 8 . . . + . . . . . . . . . . . 8 8 d . . + . . . . . . . . . . 8 8 8 d . . + . . . . . . . . . 8 8 8 8 d . . + . . . . . . . . 8 8 8 8 8 d . . + . . . . . . . 8 8 8 1 8 8 d . . + . . . . . . 8 8 8 1 8 1 8 d . . + . . . . . 8 8 8 8 1 1 1 8 d . . + . . . . 8 8 8 8 8 1 8 1 8 d . . + . . . 8 8 8 8 8 8 1 8 1 8 d . . + . . 8 8 8 8 8 8 8 8 8 8 8 d . . + . . . d d d d d d d d d d d . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_button_b = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . 8 8 8 8 8 8 8 8 8 8 8 . . . + . . 8 1 1 8 8 8 8 8 8 8 d d . . + . . 8 1 8 1 8 8 8 8 8 d d . . . + . . 8 1 1 8 8 8 8 8 d d . . . . + . . 8 1 8 1 8 8 8 d d . . . . . + . . 8 1 1 8 8 8 d d . . . . . . + . . 8 8 8 8 8 d d . . . . . . . + . . 8 8 8 8 d d . . . . . . . . + . . 8 8 8 d d . . . . . . . . . + . . 8 8 d d . . . . . . . . . . + . . 8 d d . . . . . . . . . . . + . . . d . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_timer = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . b b b b b . . . . . . + . . . . b 1 1 9 9 9 b . . . . . + . . . b 1 1 1 b 9 9 9 c . . . . + . . b 1 1 d 1 b 9 b 9 9 c . . . + . . b 1 1 1 1 9 9 9 9 9 c d . . + . . b 1 d d 1 2 2 2 2 9 c d . . + . . b 1 1 1 1 1 1 1 1 1 c d . . + . . b 1 1 d 1 d 1 d 1 1 c d . . + . . . b 1 1 1 d 1 1 1 c d . . . + . . . . c 1 1 1 1 1 c d . . . . + . . . . . c c c c c d . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_timespan_short = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . b b b b b b b b b b . . . + . . b 1 1 1 1 1 1 1 1 1 1 c . . + . . b 1 1 2 1 1 1 1 1 1 1 c . . + . . b 1 1 2 1 1 1 1 1 1 1 c d . + . . b 1 1 1 1 1 1 1 1 1 1 c d . + . . b 1 2 2 2 1 1 1 f f 1 c d . + . . b 1 1 1 1 1 1 f 1 1 1 c d . + . . b 1 2 1 2 1 1 1 f 1 1 c d . + . . b 1 2 2 2 1 1 1 1 f 1 c d . + . . b 1 1 1 2 1 1 f f 1 1 c d . + . . b 1 1 1 1 1 1 1 1 1 1 c d . + . . . c c c c c c c c c c d . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_timespan_long = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . b b b b b b b b b b . . . + . . b 1 1 1 1 1 1 1 1 1 1 c . . + . . b 1 1 1 1 1 1 1 1 1 1 c . . + . . b 1 1 1 2 1 1 1 1 1 1 c d . + . . b 1 1 2 2 1 1 1 1 1 1 c d . + . . b 1 1 1 2 1 1 1 f f 1 c d . + . . b 1 1 1 2 1 1 f 1 1 1 c d . + . . b 1 1 1 2 1 1 1 f 1 1 c d . + . . b 1 1 1 2 1 1 1 1 f 1 c d . + . . b 1 1 2 2 2 1 f f 1 1 c d . + . . b 1 1 1 1 1 1 1 1 1 1 c d . + . . . c c c c c c c c c c d . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_timespan_fiveSeconds = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . b b b b b b b b b b . . . + . . b 1 1 1 1 1 1 1 1 1 1 c . . + . . b 1 1 1 1 1 1 1 1 1 1 c . . + . . b 1 2 2 2 2 1 1 1 1 1 c d . + . . b 1 2 1 1 1 1 1 1 1 1 c d . + . . b 1 2 2 2 1 1 1 f f 1 c d . + . . b 1 1 1 1 2 1 f 1 1 1 c d . + . . b 1 1 1 1 2 1 1 f 1 1 c d . + . . b 1 1 1 1 2 1 1 1 f 1 c d . + . . b 1 2 2 2 1 1 f f 1 1 c d . + . . b 1 1 1 1 1 1 1 1 1 1 c d . + . . . c c c c c c c c c c d . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_timespan_random = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . b b b b b b b b b b . . . + . . b 1 1 1 1 1 1 1 1 1 1 c . . + . . b 1 1 1 1 1 1 1 1 1 1 c . . + . . b 1 1 2 2 1 1 1 1 1 1 c d . + . . b 1 2 1 1 2 1 1 1 1 1 c d . + . . b 1 1 1 1 2 1 1 f f 1 c d . + . . b 1 1 2 2 1 1 f 1 1 1 c d . + . . b 1 1 2 1 1 1 1 f 1 1 c d . + . . b 1 1 1 1 1 1 1 1 f 1 c d . + . . b 1 1 2 1 1 1 f f 1 1 c d . + . . b 1 1 1 1 1 1 1 1 1 1 c d . + . . . c c c c c c c c c c d . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_pin_0 = img` + . . 4 5 4 d . . . . 4 5 4 d . . + . . 4 5 4 d . . . . 4 5 4 d . . + . . 4 5 4 d . . . . 4 5 4 d . . + . . 4 5 5 4 4 4 4 4 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 5 5 f 5 5 5 5 4 d . . + . . 4 5 5 5 f 5 f 5 5 5 4 d . . + . . 4 5 5 5 f 5 f 5 5 5 4 d . . + . . 4 5 5 5 f 5 f 5 5 5 4 d . . + . . 4 5 5 5 5 f 5 5 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 4 4 4 4 4 5 5 4 d . . + . . . 4 4 d . . . . 4 4 d . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_pin_1 = img` + . . 4 5 4 d . . . . 4 5 4 d . . + . . 4 5 4 d . . . . 4 5 4 d . . + . . 4 5 4 d . . . . 4 5 4 d . . + . . 4 5 5 4 4 4 4 4 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 5 5 f 5 5 5 5 4 d . . + . . 4 5 5 5 f f 5 5 5 5 4 d . . + . . 4 5 5 5 5 f 5 5 5 5 4 d . . + . . 4 5 5 5 5 f 5 5 5 5 4 d . . + . . 4 5 5 5 f f f 5 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 4 4 4 4 4 5 5 4 d . . + . . . 4 4 d . . . . 4 4 d . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_pin_2 = img` + . . 4 5 4 d . . . . 4 5 4 d . . + . . 4 5 4 d . . . . 4 5 4 d . . + . . 4 5 4 d . . . . 4 5 4 d . . + . . 4 5 5 4 4 4 4 4 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 5 f f 5 5 5 5 4 d . . + . . 4 5 5 5 5 5 f 5 5 5 4 d . . + . . 4 5 5 5 5 f 5 5 5 5 4 d . . + . . 4 5 5 5 f 5 5 5 5 5 4 d . . + . . 4 5 5 5 f f f 5 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 4 4 4 4 4 5 5 4 d . . + . . . 4 4 d . . . . 4 4 d . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + export const radio_value = img` + . . . . . . . . . . . . . . . . + . . . . . 8 . . . 8 . . . . . . + . . . 8 . . 8 8 8 . . 8 . . . . + . 8 . . 8 . . . . . 8 . . 8 . . + . . 8 . . 8 8 8 8 8 . . 8 . . . + . . . 8 . . . . . . . 8 . . . . + . . . . 8 8 8 8 8 8 8 . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . 4 4 4 . . . . . . . + . . . . . 4 5 1 5 4 . . . . . . + . . . . . 4 1 1 1 4 . . . . . . + . . . . . 4 5 1 5 4 . . . . . . + . . . . . . 4 4 4 . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const radio_receive = img` + . . . . . . . . . . . . . . . . + . . . . . 8 . . . 8 . . . . . . + . . . 8 . . 8 8 8 . . 8 . . . . + . 8 . . 8 . . . . . 8 . . 8 . . + . . 8 . . 8 8 8 8 8 . . 8 . . . + . . . 8 . . . . . . . 8 . . . . + . . . . 8 8 8 8 8 8 8 . . . . . + . . . . . . . . . . . . . . . . + . . . . . . 4 5 4 . . . . . . . + . . . . . . 4 5 4 . . . . . . . + . . . . . . 4 5 4 . . . . . . . + . . . . 4 5 5 5 5 5 4 . . . . . + . . . . . 4 5 5 5 4 . . . . . . + . . . . . . 4 5 4 . . . . . . . + . . . . . . . 4 . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const radio_send = img` + . . . . . . . . . . . . . . . . + . . . . 8 8 8 8 8 8 8 . . . . . + . . . 8 . . . . . . . 8 . . . . + . . 8 . . 8 8 8 8 8 . . 8 . . . + . 8 . . 8 . . . . . 8 . . 8 . . + . . . 8 . . 8 8 8 . . 8 . . . . + . . . . . 8 . . . 8 . . . . . . + . . . . . . . 4 . . . . . . . . + . . . . . . 4 5 4 . . . . . . . + . . . . . 4 5 5 5 4 . . . . . . + . . . . 4 5 5 5 5 5 4 . . . . . + . . . . . . 4 5 4 . . . . . . . + . . . . . . 4 5 4 . . . . . . . + . . . . . . 4 5 4 . . . . . . . + . . . . . . 4 5 4 . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const radio_set_group = img` + . . . . . . . . . . . . . . . . + . . . . . 8 . . . 8 . . . . . . + . . . 8 . . 8 8 8 . . 8 . . . . + . 8 . . 8 . . . . . 8 . . 8 . . + . . 8 . . 8 8 8 8 8 . . 8 . . . + . . . 8 . . . . . . . 8 . . . . + . . . . 8 8 8 8 8 8 8 . . . . . + . . . . . . . . . . . . . . . . + . . . 6 6 6 . . . . 6 6 6 . . . + . . 6 9 6 9 6 . . 6 9 6 9 6 . . + . . . 6 6 6 . . . . 6 6 6 . . . + . . . . . . . . . . . . . . . . + . . . 6 6 6 . . . . 6 6 6 . . . + . . 6 9 6 9 6 . . 6 9 6 9 6 . . + . . . 6 6 6 . . . . 6 6 6 . . . + . . . . . . . . . . . . . . . . +` + + export const microbit_logo = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . 4 4 4 4 4 4 4 4 d . . . + . . . 4 d 5 5 5 5 5 5 5 4 d . . + . . 4 d . . . . . . . . 5 4 d . + . . 4 d 4 4 d . . . 4 4 d 4 d . + . . 4 d 4 4 d . . . 4 4 d 4 d . + . . 4 d . 5 5 . . . . 5 5 4 d . + . . . 4 d . . . . . . . 4 d . . + . . . . 4 4 4 4 4 4 4 4 d . . . + . . . . . 5 5 5 5 5 5 5 . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const microbit_logo_btn = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . f f f f f f f f . . . . + . . . f 1 1 1 1 1 1 1 1 f . . . + . . f 1 1 1 1 1 1 1 1 1 1 f . . + . . f 1 f f 1 1 1 1 f f 1 f . . + . . f 1 f f 1 1 1 1 f f 1 f . . + . . f 1 1 1 1 1 1 1 1 1 1 f . . + . . . f 1 1 1 1 1 1 1 1 f . . . + . . . . f f f f f f f f . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const finger_press = img` + . . . . . . . . . . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . f . f . f . . . . . . + . . . . . . f f f . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . . 4 4 4 4 4 4 4 d . . . . + . . . e 4 4 4 4 4 4 4 e d . . . + . . . e 2 4 4 4 4 4 2 e d . . . + . . . e e 2 2 2 2 2 e e d . . . + . . . . e e e e e e e d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const finger_release = img` + . . . . . . . . . . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . . f f f . . . . . . . + . . . . . f . f . f . . . . . . + . . . . . . . f . . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . . 4 4 4 4 4 4 4 d . . . . + . . . e 4 4 4 4 4 4 4 e d . . . + . . . e 2 4 4 4 4 4 2 e d . . . + . . . e e 2 2 2 2 2 e e d . . . + . . . . e e e e e e e d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_color_black = img` + . . . . . . . . . . . . . . . . + . . . . . 1 1 1 1 1 . . . . . . + . . . 1 1 f f f f f b b . . . . + . . 1 f f f f f f f f f b . . . + . . 1 f f f f f f f f f b . . . + . 1 f f f f f f f f f f f b . . + . 1 f f f f f f f f f f f b d . + . 1 f f f f f f f f f f f b d . + . 1 f f f f f f f f f f f b d . + . 1 f f f f f f f f f f f b d . + . . b f f f f f f f f f b d d . + . . b f f f f f f f f f b d . . + . . . b b f f f f f b b d . . . + . . . . . b b b b b d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_color_red = img` + . . . . . . . . . . . . . . . . + . . . . . 1 1 1 1 1 . . . . . . + . . . 1 1 2 2 2 2 2 b b . . . . + . . 1 2 2 2 2 2 2 2 2 2 b . . . + . . 1 2 2 2 2 2 2 2 2 2 b . . . + . 1 2 2 2 2 2 2 2 2 2 2 2 b . . + . 1 2 2 2 2 2 2 2 2 2 2 2 b d . + . 1 2 2 2 2 2 2 2 2 2 2 2 b d . + . 1 2 2 2 2 2 2 2 2 2 2 2 b d . + . 1 2 2 2 2 2 2 2 2 2 2 2 b d . + . . b 2 2 2 2 2 2 2 2 2 b d d . + . . b 2 2 2 2 2 2 2 2 2 b d . . + . . . b b 2 2 2 2 2 b b d . . . + . . . . . b b b b b d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_color_blue = img` + . . . . . . . . . . . . . . . . + . . . . . 1 1 1 1 1 . . . . . . + . . . 1 1 8 8 8 8 8 b b . . . . + . . 1 8 8 8 8 8 8 8 8 8 b . . . + . . 1 8 8 8 8 8 8 8 8 8 b . . . + . 1 8 8 8 8 8 8 8 8 8 8 8 b . . + . 1 8 8 8 8 8 8 8 8 8 8 8 b d . + . 1 8 8 8 8 8 8 8 8 8 8 8 b d . + . 1 8 8 8 8 8 8 8 8 8 8 8 b d . + . 1 8 8 8 8 8 8 8 8 8 8 8 b d . + . . b 8 8 8 8 8 8 8 8 8 b d d . + . . b 8 8 8 8 8 8 8 8 8 b d . . + . . . b b 8 8 8 8 8 b b d . . . + . . . . . b b b b b d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_sparkle = img` + c c c c c c c c c c c c c c c c + c c d c c c c c c c c c c c c c + c d 1 d c c c c c c c c c d c c + c c d c c c c c c c c c c c c c + c c c c c c c d c c d c c c c c + c c c c c c c c c d 1 d c c c c + c c c c c c c c c c d c c c c c + c d c c c c c c c c c c c c c c + c c c c c c c c c c c c c c c c + c c c c c c d c c 1 c c c c c c + c c c c c d 1 d c c c c c c c c + c c c c c c d c c c c c c c c c + c c c c c c c c c c c c c d c c + c c c c c c c c c c c c d 1 d c + c c c d c c c c c c c c c d c c + c c c c c c c c c c c c c c c c + ` + + export const tile_rainbow = img` + . . . . . . . . . . . . . . . . + . . 2 2 2 2 2 2 2 2 2 2 2 2 . . + . 2 2 2 2 2 2 2 2 2 2 2 2 2 2 . + 2 2 2 4 4 4 4 4 4 4 4 4 4 2 2 2 + 2 2 4 4 4 4 4 4 4 4 4 4 4 4 2 2 + 2 4 4 5 5 5 5 5 5 5 5 5 5 4 4 2 + 4 4 5 5 5 5 5 5 5 5 5 5 5 5 4 4 + 4 5 5 5 7 7 7 7 7 7 7 7 5 5 5 4 + 5 5 7 7 7 7 7 7 7 7 7 7 7 7 5 5 + 5 7 7 7 7 8 8 8 8 8 8 7 7 7 7 5 + 7 7 7 7 8 8 8 8 8 8 8 8 7 7 7 5 + 7 7 7 8 8 8 c c c c 8 8 8 7 7 7 + 7 7 8 8 8 c c c c c c 8 8 8 7 7 + 7 7 8 8 c c a a a a c c 8 8 7 7 + 7 7 8 8 c c a . . a c c 8 8 7 7 + . . . . . . . . . . . . . . . . + ` + + export const tile_color_green = img` + . . . . . . . . . . . . . . . . + . . . . . 1 1 1 1 1 . . . . . . + . . . 1 1 7 7 7 7 7 b b . . . . + . . 1 7 7 7 7 7 7 7 7 7 b . . . + . . 1 7 7 7 7 7 7 7 7 7 b . . . + . 1 7 7 7 7 7 7 7 7 7 7 7 b . . + . 1 7 7 7 7 7 7 7 7 7 7 7 b d . + . 1 7 7 7 7 7 7 7 7 7 7 7 b d . + . 1 7 7 7 7 7 7 7 7 7 7 7 b d . + . 1 7 7 7 7 7 7 7 7 7 7 7 b d . + . . b 7 7 7 7 7 7 7 7 7 b d d . + . . b 7 7 7 7 7 7 7 7 7 b d . . + . . . b b 7 7 7 7 7 b b d . . . + . . . . . b b b b b d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_color_magenta = img` + . . . . . . . . . . . . . . . . + . . . . . 1 1 1 1 1 . . . . . . + . . . 1 1 a a a a a b b . . . . + . . 1 a a a a a a a a a b . . . + . . 1 a a a a a a a a a b . . . + . 1 a a a a a a a a a a a b . . + . 1 a a a a a a a a a a a b d . + . 1 a a a a a a a a a a a b d . + . 1 a a a a a a a a a a a b d . + . 1 a a a a a a a a a a a b d . + . . b a a a a a a a a a b d d . + . . b a a a a a a a a a b d . . + . . . b b a a a a a b b d . . . + . . . . . b b b b b d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const tile_color_yellow = img` + . . . . . . . . . . . . . . . . + . . . . . 1 1 1 1 1 . . . . . . + . . . 1 1 5 5 5 5 5 b b . . . . + . . 1 5 5 5 5 5 5 5 5 5 b . . . + . . 1 5 5 5 5 5 5 5 5 5 b . . . + . 1 5 5 5 5 5 5 5 5 5 5 5 b . . + . 1 5 5 5 5 5 5 5 5 5 5 5 b d . + . 1 5 5 5 5 5 5 5 5 5 5 5 b d . + . 1 5 5 5 5 5 5 5 5 5 5 5 b d . + . 1 5 5 5 5 5 5 5 5 5 5 5 b d . + . . b 5 5 5 5 5 5 5 5 5 b d d . + . . b 5 5 5 5 5 5 5 5 5 b d . . + . . . b b 5 5 5 5 5 b b d . . . + . . . . . b b b b b d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + /* + export const tile_coin_1 = img` + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . 4 4 5 5 5 5 5 4 4 . . . . + . . 4 5 5 1 1 1 1 1 5 5 4 . . . + . . 4 5 1 1 1 1 1 1 1 5 4 . . . + . 4 5 1 1 1 1 f 1 1 1 1 5 4 . . + . 4 5 1 1 1 f f 1 1 1 1 5 4 d . + . 4 5 1 1 1 1 f 1 1 1 1 5 4 d . + . 4 5 1 1 1 1 f 1 1 1 1 5 4 d . + . 4 5 1 1 1 f f f 1 1 1 5 4 d . + . . 4 5 1 1 1 1 1 1 1 5 4 d d . + . . 4 5 5 1 1 1 1 1 5 5 4 d . . + . . . 4 4 5 5 5 5 5 4 4 d . . . + . . . . . 4 4 4 4 4 d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const tile_coin_2 = img` + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . 4 4 5 5 5 5 5 4 4 . . . . + . . 4 5 5 1 1 1 1 1 5 5 4 . . . + . . 4 5 1 1 1 1 1 1 1 5 4 . . . + . 4 5 1 1 1 f f 1 1 1 1 5 4 . . + . 4 5 1 1 1 1 1 f 1 1 1 5 4 d . + . 4 5 1 1 1 1 f 1 1 1 1 5 4 d . + . 4 5 1 1 1 f 1 1 1 1 1 5 4 d . + . 4 5 1 1 1 f f f 1 1 1 5 4 d . + . . 4 5 1 1 1 1 1 1 1 5 4 d d . + . . 4 5 5 1 1 1 1 1 5 5 4 d . . + . . . 4 4 5 5 5 5 5 4 4 d . . . + . . . . . 4 4 4 4 4 d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const tile_coin_3 = img` +. . . . . . . . . . . . . . . . +. . . . . 4 4 4 4 4 . . . . . . +. . . 4 4 5 5 5 5 5 4 4 . . . . +. . 4 5 5 1 1 1 1 1 5 5 4 . . . +. . 4 5 1 1 1 1 1 1 1 5 4 . . . +. 4 5 1 1 1 f f 1 1 1 1 5 4 . . +. 4 5 1 1 1 1 1 f 1 1 1 5 4 d . +. 4 5 1 1 1 1 f 1 1 1 1 5 4 d . +. 4 5 1 1 1 1 1 f 1 1 1 5 4 d . +. 4 5 1 1 1 f f 1 1 1 1 5 4 d . +. . 4 5 1 1 1 1 1 1 1 5 4 d d . +. . 4 5 5 1 1 1 1 1 5 5 4 d . . +. . . 4 4 5 5 5 5 5 4 4 d . . . +. . . . . 4 4 4 4 4 d d . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +` + + export const tile_coin_5 = img` + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . 4 4 5 5 5 5 5 4 4 . . . . + . . 4 5 5 1 1 1 1 1 5 5 4 . . . + . . 4 5 1 1 1 1 1 1 1 5 4 . . . + . 4 5 1 1 1 f f f 1 1 1 5 4 . . + . 4 5 1 1 1 f 1 1 1 1 1 5 4 d . + . 4 5 1 1 1 f f 1 1 1 1 5 4 d . + . 4 5 1 1 1 1 1 f 1 1 1 5 4 d . + . 4 5 1 1 1 f f 1 1 1 1 5 4 d . + . . 4 5 1 1 1 1 1 1 1 5 4 d d . + . . 4 5 5 1 1 1 1 1 5 5 4 d . . + . . . 4 4 5 5 5 5 5 4 4 d . . . + . . . . . 4 4 4 4 4 d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . .` + + export const tile_coin_4 = img` + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . 4 4 5 5 5 5 5 4 4 . . . . + . . 4 5 5 1 1 1 1 1 5 5 4 . . . + . . 4 5 1 1 1 1 1 1 1 5 4 . . . + . 4 5 1 1 1 f 1 f 1 1 1 5 4 . . + . 4 5 1 1 1 f 1 f 1 1 1 5 4 d . + . 4 5 1 1 1 f f f 1 1 1 5 4 d . + . 4 5 1 1 1 1 1 f 1 1 1 5 4 d . + . 4 5 1 1 1 1 1 f 1 1 1 5 4 d . + . . 4 5 1 1 1 1 1 1 1 5 4 d d . + . . 4 5 5 1 1 1 1 1 5 5 4 d . . + . . . 4 4 5 5 5 5 5 4 4 d . . . + . . . . . 4 4 4 4 4 d d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` +*/ + + export const rgbLed = img` + . . . . f f f f f f f . . . . . + . . f f f 4 4 f 9 9 f f f . . . + . f 5 5 f 4 4 f 9 9 f b b f . . + . f 5 5 f f f f f f f b b f d . + f f f f f . . . . . f f f f f d + f 4 4 f . . . . . . . f 7 7 f d + f 4 4 f . . . . . . . f 7 7 f d + f f f f . . . . . . . f f f f d + f 2 2 f . . . . . . . f e e f d + f 2 2 f . . . . . . . f e e f d + f f f f . . . . . . . f f f f d + b f 6 6 f f f f f f f c c f b d + . f 6 6 f 8 8 f a a f c c 5 5 5 + . b f f f 8 8 f a a f f f 5 5 5 + . . b b f f f f f f f b b 5 5 4 + . . . . d b b b b b b d d 4 4 . +` + + export const magnet = img` + . . . . . . . . . . . . 6 . . . + . . . . . . . . . . 6 . . . 6 . + . . . . . . . . . . . . . . . . + . . . 8 8 8 8 8 f f . . 6 . . . + . . 8 8 8 8 8 8 f f . . . . . 6 + . 8 8 8 b b b b b b . . 6 . . . + . 8 8 b . . . . . . . . . . 6 . + . 8 8 . . . . . . . . 6 . . . . + . 2 2 . . . . . . . . . . 6 . . + . 2 2 . . . . . . . . 6 . . . . + . 2 2 2 . . . . . . . . . . 6 . + . b 2 2 2 2 2 2 f f . . 6 . . . + . . b 2 2 2 2 2 f f . . . 5 5 5 + . . . b b b b b b b . . 6 5 5 5 + . . . . . . . . . . . . . 5 5 4 + . . . . . . . . . . 6 . . 4 4 . +` + export const thermometer = img` + . . . . . . . . . . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . . f d f . . . . . . . + . . . . . . f d f . . . . . . . + . . . . . . f d f . . . . . . . + . . . . . . f d f . . . . . . . + . . . . . . f 2 f . . . . . . . + . . . . . . f 2 f . . . . . . . + . . . . . . f 2 f . . . . . . . + . . . . . . f 2 f . . . . . . . + . . . . . f 2 2 2 f . . . . . . + . . . . f 2 2 2 2 2 f . . . . . + . . . . f 2 2 2 2 2 f . . . . . + . . . . . f 2 2 2 f . . . . . . + . . . . . . f f f . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const temp_warmer = img` +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . f . . . . . . . . +. . . . . . f 2 f . . . . . . . +. . . . . f 2 2 2 f . . . . . . +. . . . f 2 2 2 2 2 f . . . . . +. . . f 2 2 2 2 2 2 2 f . . . . +. . . f f f 2 2 2 f f f . . . . +. . . . . f 2 2 2 f . . . . . . +. . . . . f 2 2 2 f . . . . . . +. . . . . f 2 2 2 f . . . . . . +. . . . . f 2 2 2 f . . . . . . +. . . . . f 2 2 2 f . . . . . . +. . . . . f f f f f . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +` + + export const temp_colder = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . f f f f f . . . . . . + . . . . . f 9 9 9 f . . . . . . + . . . . . f 9 9 9 f . . . . . . + . . . . . f 9 9 9 f . . . . . . + . . . . . f 9 9 9 f . . . . . . + . . . . . f 9 9 9 f . . . . . . + . . . f f f 9 9 9 f f f . . . . + . . . f 9 9 9 9 9 9 9 f . . . . + . . . . f 9 9 9 9 9 f . . . . . + . . . . . f 9 9 9 f . . . . . . + . . . . . . f 9 f . . . . . . . + . . . . . . . f . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + +export const led_light_sensor = img` +. . . . . . . . . . . . . . . . +. . 8 8 8 8 5 5 5 8 8 8 8 . . . +. . 8 8 8 5 4 4 4 5 8 8 8 . . . +. . 8 8 8 5 4 4 4 5 8 8 8 . . . +. . 8 8 8 5 4 4 4 5 8 8 8 . . . +. . 8 8 8 8 5 5 5 8 8 8 8 . . . +. . 8 8 5 8 8 8 8 8 5 8 8 . . . +. . 8 5 8 8 8 5 8 8 8 5 8 . . . +. . 8 8 8 5 8 8 8 5 8 8 8 . . . +. . 8 8 5 8 8 5 8 8 5 8 8 . . . +. . 8 5 8 8 8 8 8 8 8 5 8 . . . +. . 8 8 8 8 8 5 8 8 8 8 8 . . . +. . 8 8 8 8 8 8 8 8 8 8 8 . . . +. . 8 8 8 2 2 2 2 2 8 8 8 . . . +. . 8 f f f f f f f f f 8 . . . +. . . . . . . . . . . . . . . . +` + + export const light_sensor = img` +. . . . . . . . . . . . . . . . +. . 8 8 8 8 5 5 5 8 8 8 8 . . . +. . 8 8 8 5 4 4 4 5 8 8 8 . . . +. . 8 8 8 5 4 4 4 5 8 8 8 . . . +. . 8 8 8 5 4 4 4 5 8 8 8 . . . +. . 8 8 8 8 5 5 5 8 8 8 8 . . . +. . 8 8 5 8 8 8 8 8 5 8 8 . . . +. . 8 5 8 8 8 5 8 8 8 5 8 . . . +. . 8 8 8 5 8 8 8 5 8 8 8 . . . +. . 8 8 5 8 8 5 8 8 5 8 8 . . . +. . 8 5 8 8 8 8 8 8 8 5 8 . . . +. . 8 8 8 8 8 5 8 8 8 8 8 . . . +. . 8 8 8 8 8 8 8 8 8 8 8 5 5 5 +. . 8 8 8 2 2 2 2 2 8 8 8 5 5 5 +. . 8 f f f f f f f f f 8 5 5 4 +. . . . . . . . . . . . . 4 4 . +` + + export const microphone = img` +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . b c . . . . . . . +. . . . . . b c c c . . . . . . +. . . . . . b c c c . . . . . . +. . . . . . b c c c . . . . . . +. . . . . . b c c c . . . . . . +. . . . f . c c c c . f . . . . +. . . . f . c c c c . f . . . . +. . . . f . . c c . . f . . . . +. . . . . f . . . . f . . . . . +. . . . . . f f f f . . . . . . +. . . . . . . f f . . . . . . . +. . . . . . . f f . . . . . . . +. . . . . f f f f f f . . . . . +. . . . . . . . . . . . . . . . +` + + export const speaker = img` +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . c . . . . . . . . . . +. . . . c b . . . . . 8 . . . . +. . . c b c . . . 8 . . 8 . . . +. c c b c c . 8 . . 8 . 8 . . . +. b b c c c . . 8 . 8 . 8 . . . +. c c c c c . . 8 . 8 . 8 . . . +. c c c c c . 8 . . 8 . 8 . . . +. . . c c c . . . 8 . . 8 . . . +. . . . c c . . . . . 8 . . . . +. . . . . c . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +` + + export const speakerFun = img` + . . . . . . . . . . . . . . . . + . . . . . . . . 3 . . . 5 . . . + . . . . . . . 3 . . . . . . 2 . + . . . . . c . . . . . 2 . 2 . . + . . . . c b . . 2 . 2 . 2 . . . + . . . c b c . 2 . 2 . . . . 5 . + . c c b c c . . . . . . . . . . + . b b c c c . 4 . 4 . 4 . 4 . . + . c c c c c . . 4 . 4 . 4 . 4 . + . c c c c c . . . . . . . . . . + . . . c c c . 6 . 6 . 5 . . . . + . . . . c c . . 6 . 6 . 6 . . . + . . . . . c . . . . . 6 . 6 . . + . . . . . . . 9 . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const music = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . f c . . . + . . . . . . . . . c c c c b . . + . . . . . . . c c c b b c b . . + . . . . . f c c b b b . c b . . + . . . . . c b b b . . . c b . . + . . . . . c b . . . . . c b . . + . . . . . c b . . . . . c b . . + . . . . . c b . . . f f c b . . + . . . f f c b . . f c c c b . . + . . f c c c b . . f c c b b . . + . . f c c b b . . . b b b . . . + . . . b b b . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const note_on = img` +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 c c c c 1 1 1 1 1 +1 1 1 1 1 1 c f f f f c 1 1 1 1 +1 1 1 1 1 c f f f f f f c 1 1 1 +1 1 1 1 c f f f f f f f f c 1 1 +1 1 1 1 c f f f f f f f f c 1 1 +1 1 1 c f f f f f f f f f c 1 1 +1 1 1 c f f f f f f f f f c 1 1 +1 1 1 c f f f f f f f f f c 1 1 +1 1 1 c f f f f f f f f f c 1 1 +1 1 1 c f f f f f f f f c 1 1 1 +1 1 1 c f f f f f f f f c 1 1 1 +1 1 1 1 c f f f f f f c 1 1 1 1 +1 1 1 1 1 c f f f f c 1 1 1 1 1 +1 1 1 1 1 1 c c c c 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +` + + export const note_off = img` +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 f 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +` + + export const accelerometer = img` +. . . . . . . . . . . . . . . . +. . . . . . . . 8 . . . . . . . +. . . . . . . 8 8 8 . . . . . . +. . . . . . 8 8 8 8 8 . . . . . +. . . . . . 6 6 8 6 6 . . . . . +. . . . . . . . 8 . . . . . . . +. . 8 6 . . f f f f f . . . . . +. 8 8 6 . f . . . . . f . . . . +8 8 8 8 8 f . f . f . f . . . . +. 8 8 6 . f . . . . . f . . . . +. . 8 6 . . f f f f f 8 . . 6 . +. . . . . . . . . . . . 8 6 8 . +. . . . . . . . . . . . 6 8 8 . +. . . . . . . . . . . 6 8 8 8 . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +` + + export const soundGiggle = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . . 4 5 5 5 5 5 4 . . . . . + . . . 4 5 5 5 5 5 5 5 4 . . . . + . . 4 5 5 f 5 5 5 f 5 5 4 . . . + . . 4 5 f 5 f 5 f 5 f 5 4 d . . + . . 4 3 3 5 5 5 5 5 3 3 4 d . . + . . 4 5 5 f f f f f 5 5 4 d . . + . . 4 5 5 f f 2 2 2 5 5 4 d . . + . . . 4 5 5 f 2 2 5 5 4 d . . . + . . . . 4 5 5 5 5 5 4 d . . . . + . . . . . 4 4 4 4 4 d . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const soundHappy = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . . 4 5 5 5 5 5 4 . . . . . + . . . 4 5 5 5 5 5 5 5 4 . . . . + . . 4 5 5 f 5 5 5 f 5 5 4 . . . + . . 4 5 5 f 5 5 5 f 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 f 5 5 5 f 5 5 4 d . . + . . 4 5 5 5 f f f 5 5 5 4 d . . + . . . 4 5 5 5 5 5 5 5 4 d . . . + . . . . 4 5 5 5 5 5 4 d . . . . + . . . . . 4 4 4 4 4 d . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const soundHello = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . 8 8 8 . . + . . . . . . . . . . 8 . . . 8 . + . . . . 4 4 4 4 . . . . 4 . . . + . . . 4 5 5 5 5 4 . . 4 5 4 . . + . . 4 5 f 5 5 f 5 4 . 8 8 8 d . + . . 4 5 5 5 5 5 5 4 d 8 9 8 d . + . . 4 5 f 5 5 f 5 4 8 9 9 8 d . + . . 4 5 5 f f 5 5 4 8 9 8 d . . + . . . 4 5 5 5 5 4 8 9 9 8 d . . + . . . . 4 4 4 4 9 9 9 8 d . . . + . . . 8 9 9 9 9 9 9 8 d . . . . + . . 8 9 9 9 9 9 9 9 8 d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const soundMysterious = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . 6 6 6 6 6 6 . . . . . . + . . . 6 7 7 7 7 7 7 6 . . . . . + . . f f 1 7 7 7 7 f 1 f . . . . + . . f f f f 7 7 f f f f d . . . + . . 6 f f f 7 7 f f f 6 d . . . + . . 6 7 7 7 7 7 7 7 7 6 d . . . + . . 6 7 7 f 7 7 f 7 7 6 d . . . + . . . 6 7 7 f f 7 7 6 d . . . . + . . . . 6 7 7 7 7 6 d . . . . . + . . . . . 6 7 7 6 d . . . . . . + . . . . . . 6 6 d . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const soundSad = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . . 4 5 5 5 5 5 4 . . . . . + . . . 4 5 5 5 5 5 5 5 4 . . . . + . . 4 5 5 f 5 5 5 f 5 5 4 . . . + . . 4 5 5 f 5 5 5 f 5 5 4 d . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 5 f f f 5 5 5 4 d . . + . . 4 5 5 f 5 5 5 f 5 5 4 d . . + . . . 4 5 5 5 5 5 5 5 4 d . . . + . . . . 4 5 5 5 5 5 4 d . . . . + . . . . . 4 4 4 4 4 d . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const soundSlide = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . 2 2 2 e e e . . . . + . . . . . 2 2 2 e e d e d . . . + . . . . . 2 2 e d e e e d . . . + . . . . . 2 2 e d e d e d . . . + . . . . . 2 2 e d e e e d . . . + . . . . . 2 2 e d e d e d . . . + . . . . . 2 2 e d e e e d . . . + . . . . 2 2 2 e d e d e d . . . + . . . 2 2 2 e d . e e e d . . . + . . 2 2 2 e d . . e d e d . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const soundSoaring = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 9 9 9 9 9 . . . . . . + . . . . 9 1 9 9 9 9 9 . . . . . + . . . 9 1 9 9 7 7 7 9 9 . . . . + . . . 9 1 9 7 f 7 f 7 9 . . . . + . . 6 6 6 6 6 6 6 6 6 6 6 d . . + . 6 9 5 9 5 9 5 9 5 9 5 9 6 d . + . 8 8 8 8 8 8 8 8 8 8 8 8 8 d . + . . . . . . 8 8 8 d . . . . . . + . . . . 9 . . . . . 9 . . . . . + . . . . . 9 9 9 9 9 . . . . . . + . . . 9 . . . . . . . 9 . . . . + . . . . 9 9 9 9 9 9 9 . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const soundSpring = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . 2 d . . . . . . . . . . . + . . 2 d . . 4 4 d . . . . . . . + . 2 d . . 4 d d 5 d . . . . . . + . 2 d . 4 d . . 5 d . . . . . . + . d 4 4 d . . 5 d . . . . . . . + . . d d . . 5 d . . 7 7 d . . . + . . . . . 5 d . . 7 d d 9 d . . + . . . . . 5 d . 7 d . . 9 d . . + . . . . . d 7 7 d . . 9 d . . . + . . . . . . d d . . 9 d . . . . + . . . . . . . . . . d . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const soundTwinkle = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . 3 . . . 3 . . . 3 . . . . + . . 3 5 3 . . 3 . . . . . . . . + . . . 3 . . 3 5 3 . . . . . . . + . . . . . . 3 5 3 . . . . 3 . . + . . . . 3 3 5 5 5 3 3 . . . . . + . . 3 3 5 5 5 5 5 5 5 3 3 . . . + . . . . 3 3 5 5 5 3 3 . . . . . + . . . . . . 3 5 3 . . . . . . . + . . . . . . 3 5 3 . . . 3 . . . + . . . 3 . . . 3 . . . 3 5 3 . . + . . . . . . . 3 . . . . 3 . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const soundYawn = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 4 4 4 4 4 . . . . . . + . . . . 4 5 5 5 5 5 4 . . . . . + . . . 4 5 5 5 5 5 5 5 4 . . . . + . . 4 5 f f 5 5 5 f f 5 4 . . . + . . 4 5 5 5 5 5 5 5 5 5 4 d . . + . . 4 5 5 5 f f f 5 5 5 4 d . . + . . 4 5 5 5 f f f 5 5 5 4 d . . + . . 4 5 5 5 f 2 2 5 5 5 4 d . . + . . . 4 5 5 5 5 5 5 5 4 d . . . + . . . . 4 5 5 5 5 5 4 d . . . . + . . . . . 4 4 4 4 4 d . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + export const moveShake = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . 8 . . 8 . . + . . . 9 9 9 9 9 9 . . 8 . . 8 . + . . . 9 6 6 6 6 6 . . . 8 . 8 . + . . . 9 6 f f f f f f . 8 . 8 . + . . . 9 6 f 5 5 5 5 f . . . . . + . . . 9 6 f 5 5 5 5 f 6 9 . . . + . . . . . f 5 5 5 5 f 6 9 . . . + . 8 . 8 . f f f f f f 6 9 . . . + . 8 . 8 . . . 6 6 6 6 6 9 . . . + . 8 . . 8 . . 9 9 9 9 9 9 . . . + . . 8 . . 8 . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + export const moveTiltDown = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . 8 8 8 8 . . . . . + . . . . . . 8 . . . . . . . . . + . . . . . 8 . . . . . . . . . . + . . . 8 8 8 8 8 . . . . . . . . + . . . . 8 8 8 9 9 9 9 9 9 9 . . + . . . . 9 8 9 9 9 9 9 9 9 9 . . + . . . . 9 9 9 9 9 9 9 9 9 9 . . + . f f f f f f f f f 9 9 9 9 . . + . . f 5 5 5 5 5 5 5 f 9 9 9 . . + . . . f 5 5 5 5 5 5 5 f 9 9 . . + . . . . f 5 5 5 5 5 5 5 f 9 . . + . . . . . f f f f f f f f f . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const moveTiltUp = img` +. . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . 8 8 8 8 . . . . . . + . . . . . . . . . . 8 . . . . . + . . . . . . . . . . . 8 . . . . + . . . . . . . . . 8 8 8 8 8 . . + . . 9 9 9 9 9 9 9 9 8 8 8 . . . + . . 9 9 9 9 9 9 9 9 9 8 . . . . + . . 9 9 9 9 9 9 9 9 9 . . . . . + . . 9 9 9 9 8 8 8 8 8 f f f . . + . . 9 9 9 8 6 6 6 6 6 5 f . . . + . . 9 9 8 6 6 6 6 6 6 f . . . . + . . 9 8 6 6 6 6 6 6 8 . . . . . + . . 8 8 8 8 8 8 8 8 9 . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const moveTiltLeft = img` +. . . . . . . . . . . . . . . . + . . . . . . . . . . 8 . . . . . + . . . . . . f f . 8 8 . . . . . + . . . . . f 5 f 8 8 8 8 8 . . . + . . 9 9 8 5 5 f 9 8 8 . . 8 . . + . . 9 8 6 5 5 f 9 9 8 . . . 8 . + . . 9 8 6 5 5 f 9 9 9 . . . 8 . + . . 9 8 6 5 5 f 9 9 9 . . . 8 . + . . 9 8 6 5 5 f 9 9 9 . . 8 . . + . . 9 8 6 5 5 f 9 9 9 . . . . . + . . 9 8 6 5 5 f 9 9 9 . . . . . + . . 9 9 8 5 5 f 9 9 9 . . . . . + . . . . . f 5 f . . . . . . . . + . . . . . . f f . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const moveTiltRight = img` + . . . . . . . . . . . . . . . . + . . . . . 8 . . . . . . . . . . + . . . . . 8 8 . f f . . . . . . + . . . 8 8 8 8 8 f 5 f . . . . . + . . 8 . . 8 8 9 f 5 5 8 9 9 . . + . 8 . . . 8 9 9 f 5 5 6 8 9 . . + . 8 . . . 9 9 9 f 5 5 6 8 9 . . + . 8 . . . 9 9 9 f 5 5 6 8 9 . . + . . 8 . . 9 9 9 f 5 5 6 8 9 . . + . . . . . 9 9 9 f 5 5 6 8 9 . . + . . . . . 9 9 9 f 5 5 6 8 9 . . + . . . . . 9 9 9 f 5 5 8 9 9 . . + . . . . . . . . f 5 f . . . . . + . . . . . . . . f f . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + +export const moveFaceUp = img` +. . . . . . . . . . . . . . . . +. . . . . . . 8 . . . . . . . . +. . . . . . 8 8 8 . . . . . . . +. . . . . 8 8 8 8 8 . . . . . . +. . . . . . . 8 . . . . . . . . +. . . . . . . 8 . . . . . . . . +. . . . . . . 8 . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . f f f f f f f . . . . . +. . . f 5 5 5 5 5 5 5 f . . . . +. . f 5 5 5 5 5 5 5 5 5 f . . . +. f 5 5 5 5 5 5 5 5 5 5 5 f . . +f f f f f f f f f f f f f f f . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +` +export const moveFaceDown = img` +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. f f f f f f f f f f f f f f f +. . f 5 5 5 5 5 5 5 5 5 5 5 f . +. . . f 5 5 5 5 5 5 5 5 5 f . . +. . . . f 5 5 5 5 5 5 5 f . . . +. . . . . f f f f f f f . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . 8 . . . . . . . +. . . . . . . . 8 . . . . . . . +. . . . . . . . 8 . . . . . . . +. . . . . . 8 8 8 8 8 . . . . . +. . . . . . . 8 8 8 . . . . . . +. . . . . . . . 8 . . . . . . . +. . . . . . . . . . . . . . . . +` + + export const diceToss = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . b b b b b b b b . . . + . . . . b 1 1 1 1 1 1 1 b b . . + . . . b 1 1 1 b 1 1 1 b d b . . + . . b 1 1 1 1 1 1 1 b d d b . . + . . c b b b b b b b d d b b . . + . . c b b c c c b b d d d b . . + . . c b c b b b c b d d d b . . + . . c b b b b b c b d b d b d . + . . c b b b c c b b d d d b d . + . . c b b b b b b b d d b d . . + . . c b b b c b b b d b d . . . + . . . c c c c c c c b d . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . .` + + export const cupXread = img` +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . c c c c c c c . . . . . +. . . c f f f f f f f c . . . . +. . c f f f 4 5 4 f f f c . . . +. . c c f 4 5 5 5 4 f c c . . . +. . c d c c c c c c c b c . . . +. . c d 1 d d d d d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d d f d d d b c . . . +. . c d 1 d f d f d d b c . . . +. . . d 1 d f d f d d b . . . . +. . . . 1 d d d d d d . . . . . +` + export const cupYread = img` +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . c c c c c c c . . . . . +. . . c f f f f f f f c . . . . +. . c f f f 4 5 4 f f f c . . . +. . c c f 4 5 5 5 4 f c c . . . +. . c d c c c c c c c b c . . . +. . c d 1 d d d d d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d d f d d d b c . . . +. . c d 1 d d f d d d b c . . . +. . . d 1 d d f d d d b . . . . +. . . . 1 d d d d d d . . . . . +` + export const cupZread = img` +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . c c c c c c c . . . . . +. . . c f f f f f f f c . . . . +. . c f f f 4 5 4 f f f c . . . +. . c c f 4 5 5 5 4 f c c . . . +. . c d c c c c c c c b c . . . +. . c d 1 d d d d d d b c . . . +. . c d 1 d f f f d d b c . . . +. . c d 1 d d d f d d b c . . . +. . c d 1 d d f d d d b c . . . +. . c d 1 d f d d d d b c . . . +. . . d 1 d f f f d d b . . . . +. . . . 1 d d d d d d . . . . . +` + export const cupXassign = img` +. . . . . . . . . 4 4 4 4 4 4 4 +. . . . . . . . 4 5 5 5 5 5 5 5 +. . . . . . . 4 5 4 4 4 4 4 4 4 +. . . . . . . 4 5 4 . . . . . . +. . . . c c c 4 5 4 c . . . . . +. . . c f 4 5 5 5 5 5 4 . . . . +. . c f f f 4 5 5 5 4 f c . . . +. . c c f 4 5 4 5 4 f c c . . . +. . c d c c c c c c c b c . . . +. . c d 1 d d d d d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d d f d d d b c . . . +. . c d 1 d f d f d d b c . . . +. . . d 1 d f d f d d b . . . . +. . . . 1 d d d d d d . . . . . +` + export const cupYassign = img` +. . . . . . . . . 4 4 4 4 4 4 4 +. . . . . . . . 4 5 5 5 5 5 5 5 +. . . . . . . 4 5 4 4 4 4 4 4 4 +. . . . . . . 4 5 4 . . . . . . +. . . . c c c 4 5 4 c . . . . . +. . . c f 4 5 5 5 5 5 4 . . . . +. . c f f f 4 5 5 5 4 f c . . . +. . c c f 4 5 4 5 4 f c c . . . +. . c d c c c c c c c b c . . . +. . c d 1 d d d d d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d d f d d d b c . . . +. . c d 1 d d f d d d b c . . . +. . . d 1 d d f d d d b . . . . +. . . . 1 d d d d d d . . . . . +` + export const cupZassign = img` +. . . . . . . . . 4 4 4 4 4 4 4 +. . . . . . . . 4 5 5 5 5 5 5 5 +. . . . . . . 4 5 4 4 4 4 4 4 4 +. . . . . . . 4 5 4 . . . . . . +. . . . c c c 4 5 4 c . . . . . +. . . c f 4 5 5 5 5 5 4 . . . . +. . c f f f 4 5 5 5 4 f c . . . +. . c c f 4 5 4 5 4 f c c . . . +. . c d c c c c c c c b c . . . +. . c d 1 d d d d d d b c . . . +. . c d 1 d f f f d d b c . . . +. . c d 1 d d d f d d b c . . . +. . c d 1 d d f d d d b c . . . +. . c d 1 d f d d d d b c . . . +. . . d 1 d f f f d d b . . . . +. . . . 1 d d d d d d . . . . . +` + export const cupXwritten = img` +4 4 4 4 4 4 4 . . . . . . . . . +5 5 5 5 5 5 5 4 . . . . . . . . +4 4 4 4 4 4 4 5 4 . . . . . . . +. . . . . . 4 5 4 . . . . . . . +. . . . c c 4 5 4 c c . . . . . +. . . c 4 5 5 5 5 5 4 c . . . . +. . c f f 4 5 5 5 4 f f c . . . +. . c c f 4 4 5 4 4 f c c . . . +. . c d c c c c c c c b c . . . +. . c d 1 d d d d d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d d f d d d b c . . . +. . c d 1 d f d f d d b c . . . +. . . d 1 d f d f d d b . . . . +. . . . 1 d d d d d d . . . . . +` + export const cupYwritten = img` +4 4 4 4 4 4 4 . . . . . . . . . +5 5 5 5 5 5 5 4 . . . . . . . . +4 4 4 4 4 4 4 5 4 . . . . . . . +. . . . . . 4 5 4 . . . . . . . +. . . . c c 4 5 4 c c . . . . . +. . . c 4 5 5 5 5 5 4 c . . . . +. . c f f 4 5 5 5 4 f f c . . . +. . c c f 4 4 5 4 4 f c c . . . +. . c d c c c c c c c b c . . . +. . c d 1 d d d d d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d f d f d d b c . . . +. . c d 1 d d f d d d b c . . . +. . c d 1 d d f d d d b c . . . +. . . d 1 d d f d d d b . . . . +. . . . 1 d d d d d d . . . . . +` + export const cupZwritten = img` +4 4 4 4 4 4 4 . . . . . . . . . +5 5 5 5 5 5 5 4 . . . . . . . . +4 4 4 4 4 4 4 5 4 . . . . . . . +. . . . . . 4 5 4 . . . . . . . +. . . . c c 4 5 4 c c . . . . . +. . . c 4 5 5 5 5 5 4 c . . . . +. . c f f 4 5 5 5 4 f f c . . . +. . c c f 4 4 5 4 4 f c c . . . +. . c d c c c c c c c b c . . . +. . c d 1 d d d d d d b c . . . +. . c d 1 d f f f d d b c . . . +. . c d 1 d d d f d d b c . . . +. . c d 1 d d f d d d b c . . . +. . c d 1 d f d d d d b c . . . +. . . d 1 d f f f d d b . . . . +. . . . 1 d d d d d d . . . . . +` + + export const largeEditIcon = img` + .666666666666666666666666666666. + 66666666666666666666666666666666 + 66666666666666666666666666666666 + 666666666666666666666666ee666666 + 66666666666666666666666e44e66666 + 6666666666666666666666ee442e6666 + 666666666666666666666e15e222e666 + 66666666666666666666e155ee2ee666 + 6666666666666666666e155e44eee666 + 666666666666666666e155e44eee6666 + ccccccccccccccccce155e44eeeccccc + bbbbbbbbbbbbbbbbe155e44eeebbbbbb + bbbbbbbbbbbbbbbe155e44eeebbbbbbb + 111111bbb11111e155e44eeebcbcbcbb + 1111111b11111e155e44eeebbbbbbbcb + 1111111b1111ede5e44eeebbbbbbbbbb + 1111111b1111edde44eeebbbbbbbbbcb + 1111111b1111edddeeeebbbbb1bbbbbb + 1111111b1111eedddeebcbbb111bbbcb + 1111111b1111eeeeee1bbbbbc1cbbbbb + 1111111b11111111111bcbbbbcbbbbcb + 1111111b11111111111bbbbbbbbbbbbb + 111111cbc111111111cbcbbbbbbbbbcb + ccccccbbbcccccccccbbbcbcbcbcbcbb + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + cccccccccccccccccccccccccccccccc + 66666666666666666666666666666666 + 66666666666666666666666666666666 + 66666666666666666666666666666666 + 66666666666666666666666666666666 + b666666666666666666666666666666b + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + + export const largeNewProgramIcon = img` + .11111111..............11111111. + 1bbbbbbbb..............bbbbbbbb1 + 1..............................1 + 1..............................1 + 1..............................1 + 1..............................1 + 1..............................1 + 1..............................1 + 1..............................1 + b..............................b + ................................ + ...............11............... + ...............11............... + ...............11............... + ...............11............... + ...........1111111111........... + ...........1111111111........... + ...........bbbb11bbbb........... + ...............11............... + ...............11............... + ...............11............... + ...............bb............... + ................................ + 1..............................1 + 1..............................1 + 1..............................1 + 1..............................1 + 1..............................1 + 1..............................1 + 1..............................1 + 1..............................1 + b11111111..............11111111b + .bbbbbbbb..............bbbbbbbb. +` + + export const sampleFlashingHeart = img` + .ffffffffffffffffffffffffffffff. + ffffffffffffffffffffffffffffffff + fffffffffffffffffffffff2ffffffff + ffffffffffffffffffffff212ff2ffff + ffffffffffffffffffffff212f212fff + ffffffffffffffffffffff212212ffff + fffffffffffffffffffffff2212fffff + ffffffff222222ffff222222f2222fff + fffffff22222222ff2222222221112ff + ffffff22111111222211111122222fff + fffff2211111111221111111122fffff + ffff221114444111111444411122ffff + fff22111422224111142222411122fff + fff221142ffff241142ffff241122fff + fff221142ffff241142ffff241122fff + fff2211142ffff2442ffff2411122fff + ffff221142fffff22fffff241122ffff + ffff2211142ffffffffff2411122ffff + fffff2211142ffffffff2411122fffff + ffffff2211142ffffff2411122ffffff + fffffff22111422ff22411122fffffff + ffffffff2211144224411122ffffffff + fffffffff22111144111122fffffffff + ffff222fff221111111122ffffffffff + fff21112fff2221111222fffffffffff + ffff2222ffff22211222ffffffffffff + ffffff2122ffff2222ffffffffffffff + fffff212212ffff22fffffffffffffff + ffff212f212fffffffffffffffffffff + fffff2ff212fffffffffffffffffffff + fffffffff2ffffffffffffffffffffff + bffffffffffffffffffffffffffffffb + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + export const sampleSmileyButtons = img` + .111111111111111111111111111111. + 11111111111111111111111111111111 + 11111111111111111111111111111111 + 11111bbbbbbbbbbbbbbbb11111111111 + 1111b1111111111111111b1111111111 + 1111b1111111111111111b1111111111 + 1111b1111114444111111bd111111111 + 1111b1111445555441111bd111111111 + 1111b1114555555554111bd111111111 + 1111b111455f55f554111bd111111111 + 1111b114555f55f555411bd111111111 + 1111b1145555555555411bd111111111 + 1111b1145555555555411bd111111111 + 1111b114555f55f555411bd111111111 + 1111b1114555ff5554111bd111111111 + 1111b1114555555554111bd111111111 + 1111b1111445555441111bd111111111 + 1111b1111114444111111bd111111111 + 1111b111111111111111888811111111 + 1111b111111111111118666681111111 + 11111bbbbbb1111bbb86611668111111 + 111111dddddb11bdd866166166811111 + 11111111111b11bd.8661111668d1111 + 11111111111db11bd8661661668d1111 + 111111111111dbbbd8661661668d1111 + 1111111111111ddd18866666688d1111 + 11111111111111111188666688d11111 + 1111111111111111111888888d111111 + 111111111111111111118888d1111111 + 11111111111111111111111111111111 + 11111111111111111111111111111111 + b111111111111111111111111111111b + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + /* + export const sampleDice = img` + .111111111111111111111111111111. + 11111111111111111111111111111111 + 11111111111111111111111111111111 + 11111111111111111111111111111111 + 1111ccccc11111111111111111111111 + 111111111c1111111111111111111111 + 1111111111c111111111111111111111 + 111cccc1111111111111111111111111 + 1111111cc1111cccc111111111111111 + 111111111111cdb11cc1111111111111 + 11111111111cdddb111ccc1111111111 + 111111111ccddddb111111c111111111 + 11111111cddddddb111111cd11111111 + 1111111cdddddddb1111111c11111111 + 1111111cddcdddddb111c11c11111111 + 1111111cddddddddb111111cd1111111 + 11111111cdddddcdb1111111c1111111 + 11111111cdddddddbbbb1111cd111111 + 11111111cddddddbbbbbbbbb1c111111 + 11111111cdddddbbbbcbbbbbbcd11111 + 111111111cddbbbbbbbbbbcbbcd11111 + 111111111cdbbbbbbbcbbbbbcd111111 + 111111111cbbbbcbbbbbbbbcd1111111 + 111111111cccbbbbbbcbbccd11111111 + 111111111111cccccbbbcd1111111111 + 11111111111111111cccd11111111111 + 11111111111111111111111111111111 + 11111111111111111111111111111111 + 1111111111ddddddddddddddddd11111 + 1111111111111ddddddddddddd111111 + 11111111111111111111111111111111 + b111111111111111111111111111111b + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +`*/ + + export const sampleFirefly = img` +.ffffffffffffffffffffffffffffff. +ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff +ffffffffffffffffffffff444fffffff +fffffffffffffffffffff45154ffffff +ffffffffffffffffffff4511154fffff +ffffffffffffffffffff4511154fffff +ffffffffffffffffffff4511154fffff +fffffffffffffffffffff45554ffffff +ffffffff444fffffffffff444fffffff +fffffff45154ffffffffffffffffffff +ffffff4511154fffffffffffffffffff +ffffff4511154fffffffffffffffffff +ffffff4511154fffffffffffffffffff +fffffff45554ffffffffffffffffffff +ffffffff444fffffffffffffffffffff +ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff +fffffffffffffffffffff444ffffffff +ffffffffffffffffffff45154fffffff +fffffffffffffffffff4511154ffffff +fffffffffffffffffff4511154ffffff +fffffffffffffffffff4511154ffffff +ffffffffffffffffffff45554fffffff +fffffffffffffffffffff444ffffffff +ffffffffffffffffffffffffffffffff +bffffffffffffffffffffffffffffffb +..bbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + export const sampleClapLights = img` + .ffffffff8fffffffffffffffffffff. + fffffffff8ffffffffffffffffffffff + fffffffff8ffffffffffffffffffffff + fffffffff8ffffffffffffffffffffff + fffffffff8ffffffffffffffffffffff + fffffffff8ffffffffffffffffffffff + fffffffffeffffffffffffffffffffff + ffffffffeeefffffffffffffffffffff + ffffffffeeefffffffffffffffffffff + ffffffff444fffffffffffffffffffff + fffffff45154ffffffff5fffffffffff + ffffff4511154fffffff5fffffffffff + ffffff4511154fffffff5fff5fffffff + ffffff4511154fffffff5ff5ffffffff + fffffff45554ffffffffff5fffffffff + ffffffff444fffff444fffffffffffff + fffffffffffffff44544fff5555fffff + fffffffffffff44445544fffffffffff + ffffffffffff4545545544ffffffffff + ffffffffffff4454554544444fffffff + ffffffffffff454545544545544fffff + ffffffffffff4454545545545554ffff + ffffffffffff45454545545544554fff + fffff5555fff44545455555554554fff + ffffffffffff45454555555554454fff + fffffffff5fff4545555555555454fff + ffffffff5fffff455555555555454fff + fffffff5ff5ffff45555555555454fff + ffffffffff5fffff45555555555454ff + ffffffffff5ffffff4555555555454ff + ffffffffffffffffff4445555554554f + bffffffffffffffffffff4555555554b + ..bbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + export const sampleRockPaperScissors = img` + .111111111111111111111111111111. + 11111111111111111111111111111111 + 11111111111111111111111111111111 + 11111111111111111111c11111111111 + 1111111111111111111c1ccc11111111 + 11111111ccc11111111c1111cc111111 + 1111111c422c111111c11d1111c11111 + 1111ccc42222c11111c111dd11cd1111 + 111c42c42c42cd1111c1111111cd1111 + 11c422242c42cd1111c1dd111cdd1111 + 11c42c242c42cd111c1111d11cd11111 + 11c42cc44222cd111c1111111cd11111 + 11c222cc442cd111c11dd1111cd11111 + 111c2224ccccd111c1111d11cd111111 + 1111ccccc1cdcd111cc11111cd111111 + 11111111c1cd1cd1111cc11cd1111111 + 11111111c1cc11cd11111ccd11111111 + 11111111c1cdc11cd111111111111111 + 11111111c1cd1c11cd11111111111111 + 11111111c1cd11c1cd11ccccc1111111 + 11111111c1cd111cd11cddddbc111111 + 11111111c1cd111111cd11ddbbc11111 + 11111111c1cd11111cd1dddddbbc1111 + 111111111cd11111cdddddddbbbcd111 + 1111111111111111cddddddbdbbcd111 + 1111111111111111cdddddbdbbbcd111 + 11111111111111111cdddbdbbbcd1111 + 111111111111111111cbbbbbccd11111 + 1111111111111111111cccccdd111111 + 11111111111111111111111111111111 + 11111111111111111111111111111111 + b111111111111111111111111111111b + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + export const sampleTeleportDuck = img` + .111111111111111111111111111111. + 11111111111111111111111111111111 + 11111111111111116111111111111111 + 11611111111111161611111111611111 + 11161111b11111116111116611611111 + 11116111111111511111111666111111 + 11161611111111111111511611111111 + 1111611111fff1111111666161111111 + 11161111ff555ff111161116111b1111 + 1111611f55fff55f1116111111b1b111 + 111111f55f111f55f1611115111b1111 + 111111f55f1f1f55f611111111111111 + 111111f55f111f55f111166661111111 + 1111fffff5fff555f111611116115111 + 111f44444f555555fffffff111611111 + 1111ffffff5555555555555f11611111 + 1111111f5555555555555555f1611111 + 1115116f555555554444455556111111 + 11111611f5555555555545556f666111 + 11116166f5555555555546665f111611 + 11111661f5555555556665555fd11161 + 1111611666666666665545555fd15161 + 111661111f555555544455555fd11611 + 1111166661f555555555555566666111 + 1111111116666666666666665f111611 + 111111111111ffff5555555ffd116111 + 1111111111111111fffffffdd1116111 + 1111116661111111ff44fd1111156111 + 111666111151111f44444fd111111611 + 1111161111111111fffffd1111111111 + 11111111111111111111111111111111 + b111111111111111111111111111111b + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + export const samplePetHamster = img` + .999999999999999999999999999999. + 99999999999999999999999999999999 + 99999999999999999999999999999999 + 99999999999999292999999119999999 + 99999999999992422299999999999999 + 99999999999992222299999999999999 + 99991199999999222999999999999999 + 99911119999999929999999999999999 + 99111111999999999999999999999999 + 9999999999fff99999fff99999119999 + 999999999fdddfffffdddf9991111999 + 999999999fd3ee444ee3df9911111199 + 999999999fdeddeeeddedf9999999999 + 999999999fed1fddd1fdef9999999999 + 99999999fdddffdddffdddf999999999 + 9999999fd333ddd2ddd333df99999999 + 9999999fd333dfdfdfd333df99999999 + 9999999fd333ddfffdd333df99999999 + 19999999fdddddddddddddf999999999 + 119999999fffdddddddfff9999999999 + 1111999999fffffffffff99999999911 + 999999999feeedddddeeef9999999999 + 999999999feeeedddeeeef9999999999 + 999999999ffddfeeefddff9999999999 + 777777777feffeeeeeffef6777777777 + 777771777feef44f44feef6717777177 + 7777777777fffffffffff67777777777 + 77717777777777777777777777717777 + 77777717777777777717777777151777 + 77777151777771777151777777717777 + 77777717777777777717777777777777 + b777777777777777777777777777777b + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + + export const sampleHeadsOrTails = img` + .111111111111111111111111111111. + 111111111fff11111111111111111111 + 11111111f11111111111111111111111 + 1111111f11ff11111111111111111111 + 111111111f1111111111111111111111 + 11111111111111111111111111111111 + 11111111111116611111111111111111 + 11111111111169961111111111111111 + 11111111111169996111111111111111 + 11111111111116999611111111111111 + 11111111111111699611111111111111 + 11111111111111166111f1f111111111 + 11111111111111111111f1f111111111 + 1111111111111111111f11f111111111 + 111111111111111111111f1111111111 + 11111111111111111111f11111111111 + 11111111111111441111111111111111 + 11111111111114541111111111111111 + 11111111111114554411111111111111 + 11111111111114455444111111111111 + 11111111111111445554d11111111111 + 11111111114444445555411111111111 + 111111111145555445554d1111111111 + 1111111114445555545544d111111111 + 11111111145544455455544d11111111 + 111111111445555444555544d1111111 + 1111111111444555545555554d111111 + 11111111114544444455555554dd1111 + 1111111111455555545555555544d111 + 11111111111444444455555555554d11 + 11111111111455555455455555555411 + b111111111114444444445555555554b + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + + export const sampleReactionTime = img` + .ffffffffffffffffffff455555554f. + fffffffffffffffff1ff455555554fff + fffffffffff1fffff1ff45555554ffff + fffffffffff1f1fff1ff45555554ffff + fffff1fffff1f1f1f1ff4555554fffff + fffff1fffff1f1f1f1f45555554fffff + fffff1fffff1f1fff1f4555554ffffff + fffff1fffff1f1ffff45555554f1ffff + f1fff1fffff1fff4445555554ff1ffff + f1fff1fffff1ff45555555554ff1ffff + f1fff1fffffff455555555554ff1ffff + f1fff1ffffff455555555554fff1ff1f + f1fff1fffff4555555555554fff1ff1f + ff1fff1fff45455555555554fff1ff1f + ff1fff1ff45454555555554ffff1ff1f + ff1fff1ff44545455555554ffff1ff1f + ff1fff1ff4545454554554ffff1fff1f + ff1fff1ff445454554554fffff1fff1f + ff1fff1ff45454554454ffffff1ff1ff + ff1fff1ff4454554f44fffffff1ff1ff + ff1fff1ff454554fffffffffff1ff1ff + fff1ff1fff4444ffffffffffff1ff1ff + fff1ff1fffffffffffffffffff1ff1ff + fff1ffffffffffffffffffffff1ff1ff + fff1fffffffff4444444ffffff1fffff + ffffffffffff444444444fffffffffff + fffffffffffe444444444effffffffff + ddddddddddfe244444442efddddddddd + ddddddddddfee2222222eefddddddddd + dddddddddddfeeeeeeeeefdddddddddd + ddddddddddddfffffffffddddddddddd + bddddddddddddddddddddddddddddddb + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + export const sampleHotPotato = img` + .ffffffffffffffffffffffffffffff. + ffffffffffffffff5fffffffffffffff + ffffffffffffffff55ffffffffffffff + ffffffffff5f255f22ffffffffffffff + fffffff25f22f255f2ffffffffffffff + ffffff522ff22222522ff2ffffffffff + ffffff5525552542252252ff5f2fffff + ffffff24252555542252552f5522ffff + ffff4425455244455252252f55524fff + ffff444554422444444544525254ffff + fffff444554ddddddd4444525254ffff + ffffff54444dddedddd44442525fffff + ffffff5444dddddddeddd4424222ffff + fffff45544eddddddddddd42442fffff + ffffff4544eddeddddddddd44455ffff + ffffff4544eddddddeddddd44445ffff + ff54455454eddddddddddded4455ffff + fff54444444edddddddedddd445fffff + ffff5522544eeddddddddddd44ffffff + fffff5524444edddedddddddd4ffffff + ffffff555444eeddddddedded4ffffff + fffffffff5544edddddddddde4ffffff + ffffffffff554edddedddddd44ffffff + fffffffffff54eedddddeddd44ffffff + ffffffffffff44eddddddddd44ffffff + ffffffffffff44eedddddeee44ffffff + fffffffffffff44eeddeeee44fffffff + fffffffffffff444eeee44444fffffff + ffffffffffffff4444444444ffffffff + ffffffffffffffff44444fffffffffff + ffffffffffffffffffffffffffffffff + bffffffffffffffffffffffffffffffb + .bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. +` + + export const sampleRailCrossingLight = img` + .999999991999999999999999999999. + 99999999999999199999999919999999 + 9999ccccc99999999999999999991999 + 999c44444c9999999991999991999999 + 99c4222224c999999999999999999991 + 99c4222224c999999999999999999999 + 99c4222224c999999999999999999999 + 99c4222224c999999999999999999999 + 99c4222224c999999999999999999999 + 999c44444c9999999999999999999999 + 9999ccccc9999999999999999992d999 + 99999bcb99999999999999999bbd2999 + 9999ccccc99999999999999bbdddb999 + 999c44444c999999999999b2ddbb9999 + 99c4888884c999999999bbdd2b999999 + 99c4888884c99999999b2ddb99999999 + 99c4888884c999999bbdd2b999999999 + 99c4888884c9999bb2ddb99999999999 + 99c4888884c999bddd2b999999999999 + 999c44444c99bbddbb99999999999999 + 9999ccccc99bdddb9999999999999999 + 99999bcbfbb2dbb99999999999999999 + 99999bcbbddd29999999999999999999 + 99999cbdddbb99999999999999999999 + 9999bbddbb9999999999999999999999 + 999b2ddb999999999999999999999999 + 999dd2bc999999999999999999999999 + 9999bccc999999999999999999999999 + 97999ccc999999999999999999999999 + 79979ccc999999999999999999999555 + 99799ccc999999999999999999999555 + b7777ccceeeeeeeeeeeeeeeeeeeee554 + .bbbbbbbbbbbbbbbbbbbbbbbbbbbb44. + ` + export const settingsGear = img` + . . . . . . . . . . . . . . . . + . . . . . . . d d . . . . . . . + . . . d d . d b b c . d d . . . + . . d b b c d b b c d b b c . . + . . d b b b b b b b b b b c . . + . . . c b d b c c b d b c . . . + . . d d b b c . . c b b d d . . + . d b b b c . . . . c b b b c . + . d b b b c . . . . c b b b c . + . . c c b b c . . c b b c c . . + . . . d b d b c c b d b c . . . + . . d b b b b b b b b b b c . . + . . d b b c c b b c c b b c . . + . . . c c . c b b c . c c . . . + . . . . . . . c c . . . . . . . + . . . . . . . . . . . . . . . . +` + + const one = img` +. . . . . . +. . f f . . +. f f f . . +. . f f . . +. . f f . . +. . f f . . +. f f f f . +. . . . . . +` + + const two = img` +. . . . . . +. . f f . . +. f . . f . +. . . . f . +. . f f . . +. f f . . . +. f f f f . +. . . . . . +` + const three = img` +. . . . . . +. f f f . . +. . . . f . +. . f f f . +. . . . f . +. . . . f . +. f f f . . +. . . . . . +` + const four = img` +. . . . . . +. f . . f . +. f . . f . +. f f f f . +. . . . f . +. . . . f . +. . . . f . +. . . . . . +` + const five = img` +. . . . . . +. f f f f . +. f . . . . +. f f f . . +. . . . f . +. . . . f . +. f f f . . +. . . . . . +` + + export const servo_set_angle = img` + . . . . . . . . . . . . . . . . + . . . 8 8 8 . . . . 4 . . . . . + . . 8 8 8 8 8 . . . 2 . . . . . + . . 8 8 8 8 8 . . . 2 4 . . . . + . . 8 8 8 8 8 . . . . 2 . . . . + . . 8 8 8 8 8 . . . . 2 . . . . + . . 8 8 8 8 8 . . . . 2 . . . . + . . 8 b b b 8 . . 4 . 2 . 4 . . + . . 8 b c b b . . 2 4 2 4 2 . . + . . 8 b c c b . . . 2 2 2 . . . + . . 8 8 b b c b . . . 2 . . . . + . . 8 8 8 8 b c b . . . . . . . + . . 8 8 8 8 8 b c b . . . 5 5 5 + . . 8 8 8 8 8 . b c b . . 5 5 5 + . . . 8 8 8 . . . b c . . 5 5 4 + . . . . . . . . . . . b . 4 4 . + ` + + export const blocks1 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . 4 4 4 4 . . . . . . + . . . . . . 4 5 5 4 . . . . . . + . . . . . . 4 5 5 4 . . . . . . + . . . . . . 4 4 4 4 . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const blocks2 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . 4 4 4 4 . . 4 4 4 4 . . . + . . . 4 5 5 4 . . 4 5 5 4 . . . + . . . 4 5 5 4 . . 4 5 5 4 . . . + . . . 4 4 4 4 . . 4 4 4 4 . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const blocks3 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . 4 4 4 4 . 4 4 4 4 . 4 4 4 4 . + . 4 5 5 4 . 4 5 5 4 . 4 5 5 4 . + . 4 5 5 4 . 4 5 5 4 . 4 5 5 4 . + . 4 4 4 4 . 4 4 4 4 . 4 4 4 4 . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const blocks4 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . 4 4 4 4 . . 4 4 4 4 . . . + . . . 4 5 5 4 . . 4 5 5 4 . . . + . . . 4 5 5 4 . . 4 5 5 4 . . . + . . . 4 4 4 4 . . 4 4 4 4 . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . 4 4 4 4 . . 4 4 4 4 . . . + . . . 4 5 5 4 . . 4 5 5 4 . . . + . . . 4 5 5 4 . . 4 5 5 4 . . . + . . . 4 4 4 4 . . 4 4 4 4 . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const blocks5 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . 4 4 4 4 . 4 4 4 4 . . . . + . . . 4 5 5 4 . 4 5 5 4 . . . . + . . . 4 5 5 4 . 4 5 5 4 . . . . + . . . 4 4 4 4 . 4 4 4 4 . . . . + . . . . . . . . . . . . . . . . + . 4 4 4 4 . 4 4 4 4 . 4 4 4 4 . + . 4 5 5 4 . 4 5 5 4 . 4 5 5 4 . + . 4 5 5 4 . 4 5 5 4 . 4 5 5 4 . + . 4 4 4 4 . 4 4 4 4 . 4 4 4 4 . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const kita_slider = img` + . . . . . . 6 6 6 6 6 . . . . . + . . . . . 6 6 6 f 6 6 6 . . . . + . . . . . 6 6 f f f 6 6 . . . . + . . . . . 6 6 f c f 6 6 . . . . + . . . . . 6 6 f c f 6 6 . . . . + . . . . . 6 6 f c f 6 6 . . . . + . . . . . 6 6 f c f 6 6 . . . . + . . . . . 6 9 9 9 9 9 6 . . . . + . . . . . 6 9 9 9 9 9 6 . . . . + . . . . . 6 6 f c f 6 6 . . . . + . . . . . 6 6 f c f 6 6 . . . . + . . . . . 6 6 f c f 6 6 . . . . + . . . . . 6 6 f c f 6 6 . 5 5 5 + . . . . . 6 6 f f f 6 6 . 5 5 5 + . . . . . 6 6 6 f 6 6 6 . 5 5 4 + . . . . . . 6 6 6 6 6 . . 4 4 . + ` + export const kita_key_1 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . 6 6 6 6 6 6 6 6 . . . . . + . . 6 6 6 6 6 6 6 6 6 6 . . . . + . . 6 c f f f f f f c 6 . . f . + . . 6 f f c c c c f f 6 . f f . + . . 6 f c f f f f c f 6 . . f . + . . 6 f c f f f f c f 6 . . f . + . . 6 f c f f f f c f 6 . f f f + . . 6 f c f f f f c f 6 . . . . + . . 6 f f c c c c f f 6 . . . . + . . 6 c f f f f f f c 6 . . . . + . . 6 6 6 6 6 6 6 6 6 6 . 5 5 5 + . . . 6 6 6 6 6 6 6 6 . . 5 5 5 + . . . . . . . . . . . . . 5 5 4 + . . . . . . . . . . . . . 4 4 . + ` + export const kita_key_2 = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . 6 6 6 6 6 6 6 6 . . . . . + . . 6 6 6 6 6 6 6 6 6 6 . . . . + . . 6 c f f f f f f c 6 . f f . + . . 6 f f c c c c f f 6 . . . f + . . 6 f c f f f f c f 6 . . f . + . . 6 f c f f f f c f 6 . f . . + . . 6 f c f f f f c f 6 . f f f + . . 6 f c f f f f c f 6 . . . . + . . 6 f f c c c c f f 6 . . . . + . . 6 c f f f f f f c 6 . . . . + . . 6 6 6 6 6 6 6 6 6 6 . 5 5 5 + . . . 6 6 6 6 6 6 6 6 . . 5 5 5 + . . . . . . . . . . . . . 5 5 4 + . . . . . . . . . . . . . 4 4 . + ` + + export const kita_rotary = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . 6 6 6 6 6 6 6 . . . . . + . . . 6 6 6 6 6 6 6 6 6 . . . . + . . 6 6 6 d d d d d 6 6 6 . . . + . . 6 6 d f f 1 f f d 6 6 . . . + . . 6 d f f f 1 f f f d 6 . . . + . . 6 d f f f 1 f f f d 6 . . . + . . 6 d f f f f f f f d 6 . . . + . . 6 d f f f f f f f d 6 . . . + . . 6 6 d f f f f f d 6 6 . . . + . . 6 6 6 d d d d d 6 6 6 . . . + . . . 6 6 6 6 6 6 6 6 6 . 5 5 5 + . . . . 6 6 6 6 6 6 6 . . 5 5 5 + . . . . . . . . . . . . . 5 5 4 + . . . . . . . . . . . . . 4 4 . + ` + + export const kita_rotary_left = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . 9 9 9 9 8 . . . . . + . . . . . 9 9 9 9 9 8 . . . . . + . . . . 9 9 9 8 . . . . . . . . + . . . . 9 9 8 . . . . . . . . . + . . . . 9 9 8 . . . . . . . . . + . . 9 9 9 9 9 9 8 . . . . . . . + . . . 9 9 9 9 8 . . . . . . . . + . . . . 9 9 8 . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const kita_rotary_right = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . 8 9 9 9 9 . . . . . . + . . . . . 8 9 9 9 9 9 . . . . . + . . . . . . . . 8 9 9 9 . . . . + . . . . . . . . . 8 9 9 . . . . + . . . . . . . . . 8 9 9 . . . . + . . . . . . . 8 9 9 9 9 9 9 . . + . . . . . . . . 8 9 9 9 9 . . . + . . . . . . . . . 8 9 9 . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const car = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . f f f f f f f f . . . . + . . . f f 1 1 1 1 1 1 f f . . . + . . f f 1 1 1 1 1 1 1 1 f f . . + . . f f 1 1 1 1 1 1 1 d f f . . + . . f f 1 d d d d d d d f f . . + . f f f f f f f f f f f f f f . + . f f 9 f f f f f f f f 9 f f . + . f 9 1 9 f f f f f f 9 1 9 f d + . f f 9 f f f f f f f f 9 f f d + . f f f f f f f f f f f f f f d + . . f f d . . . . . . . f f d d + . . f f d . . . . . . . f f d . + . . f f . . . . . . . . f f . . + . . . . . . . . . . . . . . . . +` + + export const car_forward = img` + . . . . . . . . . . . . . . . . + . . . . . . . c . . . . . . . . + . . . . . . c 7 c . . . . . . . + . . . . . c 7 7 7 c . . . . . . + . . . . c 7 7 7 7 7 c . . . . . + . . . c 7 7 7 7 7 7 7 c . . . . + . . . c 7 7 7 7 7 7 7 c . . . . + . . . c c c 7 7 7 c c c d . . . + . . . . . c 7 7 7 c d d d . . . + . . . . . c 7 7 7 c d . . . . . + . . . . . c 7 7 7 c d . . . . . + . . . . . c 7 7 7 c d . . . . . + . . . . . c 7 7 7 c d . . . . . + . . . . . c c c c c . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + export const car_forward_fast = img` + . . . . . . . c . . . . . . . . + . . . . . . c 7 c . . . . . . . + . . . . . c 7 7 7 c . . . . . . + . . . . c 7 7 7 7 7 c . . . . . + . . . c 7 7 7 7 7 7 7 c . . . . + . . . c 7 7 7 7 7 7 7 c . . . . + . . . c c c c c c c c c d . . . + . . . . . . . . . . . . . . . . + . . . . . c c c c c . . . . . . + . . . . . c 7 7 7 c d . . . . . + . . . . . c c c c c d . . . . . + . . . . . . . . . . . . . . . . + . . . . . c 7 7 7 c . . . . . . + . . . . . c c c c c d . . . . . + . . . . . . . . . . . . . . . . + . . . . . c 7 7 7 c d . . . . . + ` + + export const car_reverse = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . c c c c c . . . . . + . . . . . . c 7 7 7 c . . . . . + . . . . . . c 7 7 7 c d . . . . + . . . . . . c 7 7 7 c d . . . . + . . . . . . c 7 7 7 c d . . . . + . . . . . . c 7 7 7 c d . . . . + . . . . c c c 7 7 7 c c c . . . + . . . . c 7 7 7 7 7 7 7 c . . . + . . . . c 7 7 7 7 7 7 7 c . . . + . . . . . c 7 7 7 7 7 c . . . . + . . . . . . c 7 7 7 c . . . . . + . . . . . . . c 7 c . . . . . . + . . . . . . . . c . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const car_left_turn = img` + . . . . . . . . . . . . . . . . + . . . . . c c c . . . . . . . . + . . . . c 7 7 c . . . . . . . . + . . . c 7 7 7 c c c c . . . . . + . . c 7 7 7 7 7 7 7 7 c . . . . + . c 7 7 7 7 7 7 7 7 7 7 c . . . + . . c 7 7 7 7 7 7 7 7 7 7 c . . + . . . c 7 7 7 c c 7 7 7 7 7 c . + . . . . c 7 7 c d c 7 7 7 7 c . + . . . . . c c c . . c 7 7 7 c d + . . . . . . . . . . c 7 7 7 c d + . . . . . . . . . . c 7 7 7 c d + . . . . . . . . . . c 7 7 7 c d + . . . . . . . . . . c 7 7 7 c d + . . . . . . . . . . c c c c c . + . . . . . . . . . . . . . . . . +` + + export const car_left_spin = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . c c c c c . . . . . . + . . . . c 7 7 7 7 7 c . . . . . + . . . c 7 7 7 7 7 7 7 c . . . . + . . c 7 7 7 7 7 7 7 7 7 c . . . + . . c 7 7 7 c c 7 7 7 7 7 c . . + c c c 7 7 7 c c c 7 7 7 7 7 c . + c 7 7 7 7 7 7 7 c c 7 7 7 7 c . + c 7 7 7 7 7 7 7 c d c 7 7 7 c d + . c 7 7 7 7 7 c d . c 7 7 7 c d + . . c 7 7 7 c d . . c 7 7 7 c d + . . . c 7 c d . . . c 7 7 7 c d + . . . . c . . . . . c 7 7 7 c d + . . . . . . . . . . c c c c c . + . . . . . . . . . . . . . . . . + ` + + export const car_right_turn = img` + . . . . . . . . . . . . . . . . + . . . . . . . . c c c . . . . . + . . . . . . . . c 7 7 c . . . . + . . . . . c c c c 7 7 7 c . . . + . . . . c 7 7 7 7 7 7 7 7 c . . + . . . c 7 7 7 7 7 7 7 7 7 7 c . + . . c 7 7 7 7 7 7 7 7 7 7 c . . + . c 7 7 7 7 7 c c 7 7 7 c . . . + . c 7 7 7 7 c . c 7 7 c . . . . + . c 7 7 7 c d . c c c . . . . . + . c 7 7 7 c d . . . . . . . . . + . c 7 7 7 c d . . . . . . . . . + . c 7 7 7 c d . . . . . . . . . + . c 7 7 7 c d . . . . . . . . . + . c c c c c . . . . . . . . . . + . . . . . . . . . . . . . . . . +` + + export const car_right_spin = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . c c c c c . . . . . . + . . . . c 7 7 7 7 7 c . . . . . + . . . c 7 7 7 7 7 7 7 c . . . . + . . c 7 7 7 7 7 7 7 7 7 c . . . + . c 7 7 7 7 7 c c 7 7 7 c . . . + c 7 7 7 7 7 c c c 7 7 7 c c c . + c 7 7 7 7 c c 7 7 7 7 7 7 7 c d + c 7 7 7 c d c 7 7 7 7 7 7 7 c d + c 7 7 7 c d . c 7 7 7 7 7 c d . + c 7 7 7 c d . . c 7 7 7 c d . . + c 7 7 7 c d . . . c 7 c d . . . + c 7 7 7 c . . . . . c d . . . . + c c c c c . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + + export const car_stop = img` +. . . . . . . . . . . . . . . . +. . . . . d d d d d d . . . . . +. . . . d 1 1 1 1 1 1 d . . . . +. . . d 1 2 2 2 2 2 2 1 d . . . +. . d 1 2 2 2 2 2 2 2 2 1 d . . +. d 1 2 2 2 2 2 2 2 2 2 2 1 d . +. d 1 2 2 2 2 2 2 2 2 2 2 1 d . +. d 1 2 2 1 1 1 1 1 1 2 2 1 d . +. d 1 2 2 1 1 1 1 1 1 2 2 1 d . +. d 1 2 2 2 2 2 2 2 2 2 2 1 d . +. d 1 2 2 2 2 2 2 2 2 2 2 1 d . +. . d 1 2 2 2 2 2 2 2 2 1 d . . +. . . d 1 2 2 2 2 2 2 1 d . . . +. . . . d 1 1 1 1 1 1 d . . . . +. . . . . d d d d d d . . . . . +. . . . . . . . . . . . . . . . +` + + export const car_wall = img` + . . . . . . . . . . . . . . . . + d d d d d d d d d d d d d d d d + 2 2 2 2 d 2 2 2 2 d 2 2 2 2 d 2 + 2 2 2 2 d 2 2 2 2 d 2 2 2 2 d 2 + d d d d d d d d d d d d d d d d + 2 2 d 2 2 2 2 d 2 2 2 2 d 2 2 2 + 2 2 d 2 2 2 2 d 2 2 2 2 d 2 2 2 + d d d d d d d d d d d d d d d d + 2 2 2 2 d 2 2 2 2 d 2 2 2 2 d 2 + 2 2 2 2 d 2 2 2 2 f f f f f f 2 + d d d d d d d d d f 1 1 1 1 f d + 2 2 d 2 2 2 2 d 2 f d d d d f 2 + 2 2 d 2 2 2 2 d f 9 f f f f 9 f + d d d d d d d d f 1 f f f f 1 f + . . . . . . . . f f f f f f f f + . . . . . . . . . f . . . . f . +` + + export const line_sensor = img` + . . . . . . . . . . . . . . . . + . b d d d d c f f c d d d d b . + . b d d d d c f f c d d d d b . + . b d d d d c f f c d d d d b . + . d d d d d c f f c d d d d d . + . d d d d d c f f c d d d d d . + . d d d d d c f f c d d d d d . + . b d d d d c f f c d d d d b . + . b d d d d c f c c d d d d b . + . b d d d d c f c f f f f f f . + . d d d d d c f c f 1 1 1 1 f . + . d d d d d c f c f d d d d f . + . d d d d d c c f 9 f f f f 9 f + . b d d d d c c f 1 f f f f 1 f + . b d d d d c c f f f f f f f f + . . . . . . . . . f . . . . f . +` + export const line_neither_on = img` +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . +. . . . . d d . d d . . . . . . +. . . . d 1 d . d 1 d . . . . . +. . . d 1 1 d . d 1 1 d . . . . +. . d 1 1 1 d . d 1 1 1 d . . . +. d 1 1 1 1 d . d 1 1 1 1 d . . +. d 1 1 1 1 d . d 1 1 1 1 d . . +. d 1 1 1 1 d . d 1 1 1 1 d . . +. d 1 1 1 1 d . d 1 1 1 1 d . . +. d 1 1 1 d . . . d 1 1 1 d . . +. d 1 1 d . . . . . d 1 1 d . . +. d 1 d . . . . . . . d 1 d . . +. d d . . . . . . . . . d d . . +. . . . . . . . . . . . . . . . +` + export const line_left_on = img` + . c f f f c . . . . . . . . . . + . c f f f c . . . . . . . . . . + . c f f f b . . . . . . . . . . + . c f f f d d . d d . . . . . . + . c f f d 7 d . d 1 d . . . . . + . c f d 7 7 d . d 1 1 d . . . . + . b d 7 7 7 d . d 1 1 1 d . . . + . d 7 7 7 7 d . d 1 1 1 1 d . . + . d 7 7 7 7 d . d 1 1 1 1 d . . + . d 7 7 7 7 d . d 1 1 1 1 d . . + . d 7 7 7 7 d . d 1 1 1 1 d . . + . d 7 7 7 d . . . d 1 1 1 d . . + . d 7 7 d b . . . . d 1 1 d . . + . d 7 d f c . . . . . d 1 d . . + . d d f f c . . . . . . d d . . + . b f f f c . . . . . . . . . .` + export const line_right_on = img` + . . . . . . . . . c f f f c . . + . . . . . . . . . c f f f c . . + . . . . . . . . . b f f f c . . + . . . . . d d . d d f f f c . . + . . . . d 1 d . d 7 d f f c . . + . . . d 1 1 d . d 7 7 d f c . . + . . d 1 1 1 d . d 7 7 7 d b . . + . d 1 1 1 1 d . d 7 7 7 7 d . . + . d 1 1 1 1 d . d 7 7 7 7 d . . + . d 1 1 1 1 d . d 7 7 7 7 d . . + . d 1 1 1 1 d . d 7 7 7 7 d . . + . d 1 1 1 d . . . d 7 7 7 d . . + . d 1 1 d . . . . b d 7 7 d . . + . d 1 d . . . . . c f d 7 d . . + . d d . . . . . . c f f d d . . + . . . . . . . . . c f f f b . . +` + export const line_both_on = img` + . . . . . c f f f c . . . . . . + . . . . . c f f f c . . . . . . + . . . . . b f f f b . . . . . . + . . . . . d d f d d . . . . . . + . . . . d 7 d f d 7 d . . . . . + . . . d 7 7 d f d 7 7 d . . . . + . . d 7 7 7 d f d 7 7 7 d . . . + . d 7 7 7 7 d f d 7 7 7 7 d . . + . d 7 7 7 7 d f d 7 7 7 7 d . . + . d 7 7 7 7 d f d 7 7 7 7 d . . + . d 7 7 7 7 d f d 7 7 7 7 d . . + . d 7 7 7 d f f f d 7 7 7 d . . + . d 7 7 d b f f f b d 7 7 d . . + . d 7 d . c f f f c . d 7 d . . + . d d . . c f f f c . . d d . . + . . . . . c f f f c . . . . . . +` + + export const line_none_from_left = img` +. c f f f c . . . . . . . . . . +. c f f f c . . . . . . . . . . +. c f f f c . . . . . . . . . . +. c f f f c . . . . . . d d . d +. c f f f c . . . . . d 1 d . d +. c f f f c . . . . d 1 1 d . d +. c f f f c . . . d 1 1 1 d . d +. c f f f c . . d 1 1 1 1 d . d +. c f f f c . . d 1 1 1 1 d . d +. c f f f c . . d 1 1 1 1 d . d +. c f f f c . . d 1 1 1 1 d . d +. c f f f c . . d 1 1 1 d . . . +. c f f f c . . d 1 1 d . . . . +. c f f f c . . d 1 d . . . . . +. c f f f c . . d d . . . . . . +. c f f f c . . . . . . . . . . +` + + export const line_none_from_right = img` + . . . . . . . . . . c f f f c . + . . . . . . . . . . c f f f c . + . . . . . . . . . . c f f f c . + d . d d . . . . . . c f f f c . + d . d 1 d . . . . . c f f f c . + d . d 1 1 d . . . . c f f f c . + d . d 1 1 1 d . . . c f f f c . + d . d 1 1 1 1 d . . c f f f c . + d . d 1 1 1 1 d . . c f f f c . + d . d 1 1 1 1 d . . c f f f c . + d . d 1 1 1 1 d . . c f f f c . + . . . d 1 1 1 d . . c f f f c . + . . . . d 1 1 d . . c f f f c . + . . . . . d 1 d . . c f f f c . + . . . . . . d d . . c f f f c . + . . . . . . . . . . c f f f c . + ` + + /* maybe use these later + export const rc_high = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . f f f f f f f . . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 5 5 5 5 5 5 5 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 9 6 9 6 9 6 9 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . c f f f f f f f c . . . . + . . . . c c c c c c c . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const rc_low = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . f f f f f f f . . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 9 6 9 6 9 6 9 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 5 5 5 5 5 5 5 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . c f f f f f f f c . . . . + . . . . c c c c c c c . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const rc_low_to_high = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . f f f f f f f . . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 6 6 6 5 5 5 5 f . . . . + . . . f 6 6 6 5 6 6 6 f . . . . + . . . f 9 6 9 5 9 6 9 f . . . . + . . . f 6 6 6 5 6 6 6 f . . . . + . . . f 5 5 5 5 6 6 6 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . c f f f f f f f c . . . . + . . . . c c c c c c c . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const rc_high_to_low = img` + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . f f f f f f f . . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . f 5 5 5 5 6 6 6 f . . . . + . . . f 6 6 6 5 6 6 6 f . . . . + . . . f 9 6 9 5 9 6 9 f . . . . + . . . f 6 6 6 5 6 6 6 f . . . . + . . . f 6 6 6 5 5 5 5 f . . . . + . . . f 6 6 6 6 6 6 6 f . . . . + . . . c f f f f f f f c . . . . + . . . . c c c c c c c . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . + ` + export const loud = img` +. . . . . . . . . . . . . . . . +. 2 2 2 2 2 2 2 2 2 2 2 2 2 2 . +. 2 f f 2 2 2 2 2 2 f f f 2 2 . +. 2 f f 2 2 2 2 2 f f 2 f f 2 . +. 2 f f 2 2 2 2 2 f 2 2 2 f 2 . +. 2 f f 2 2 2 2 2 f f 2 f f 2 . +. 2 f f f f 2 2 2 2 f f f 2 2 . +. 2 2 2 2 2 2 2 2 2 2 2 2 2 2 . +. 2 2 2 2 2 2 2 2 2 2 2 2 2 2 . +. 2 f f 2 2 f 2 2 f f f f 2 2 . +. 2 f f 2 2 f 2 2 f f 2 2 f 2 . +. 2 f f 2 2 f 2 2 f f 2 2 f 2 . +. 2 f f 2 2 f 2 2 f f 2 2 f 2 . +. 2 2 f f f 2 2 2 f f f f 2 2 . +. 2 2 2 2 2 2 2 2 2 2 2 2 2 2 . +. . . . . . . . . . . . . . . . +` + + export const quiet = img` +9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 +9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 +9 9 9 9 1 1 1 9 9 9 9 9 9 9 1 9 +9 9 9 1 1 1 1 1 9 9 1 1 1 9 9 9 +9 9 1 1 1 1 1 1 9 9 9 1 1 1 9 9 +9 9 9 9 1 1 9 9 9 9 9 9 1 1 1 9 +9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 +9 9 9 9 9 9 9 1 1 1 1 9 9 9 9 9 +9 9 9 9 9 9 9 9 1 1 1 9 9 9 9 9 +9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 +9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 +9 9 9 7 7 7 7 7 7 9 9 9 9 9 9 9 +7 7 7 7 7 7 7 7 7 7 7 9 9 9 7 7 +7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 +7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 +7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 +` + + */ +} diff --git a/libs/microcode/bounds.ts b/libs/microcode/bounds.ts new file mode 100644 index 00000000000..83a846276db --- /dev/null +++ b/libs/microcode/bounds.ts @@ -0,0 +1,233 @@ +namespace microcode { + export class Bounds { + public width: number + public height: number + public left: number + public top: number + //% blockCombine block="right" callInDebugger + public get right() { + return this.left + this.width - 1 + } + public set right(val: number) { + this.width = val - this.left + 1 + } + //% blockCombine block="bottom" callInDebugger + public get bottom() { + return this.top + this.height - 1 + } + public set bottom(val: number) { + this.height = val - this.top + 1 + } + public get topLeft() { + return new Vec2(this.left, this.top) + } + public get topRight() { + return new Vec2(this.right, this.top) + } + public get bottomLeft() { + return new Vec2(this.left, this.bottom) + } + public get bottomRight() { + return new Vec2(this.right, this.bottom) + } + + constructor(opts?: { + width: number + height: number + left: number + top: number + }) { + opts = opts || { width: 0, height: 0, left: 0, top: 0 } + this.width = opts.width + this.height = opts.height + this.left = opts.left + this.top = opts.top + } + + public clone(): Bounds { + return new Bounds({ + left: this.left, + top: this.top, + width: this.width, + height: this.height, + }) + } + + public copyFrom(other: Bounds) { + this.left = other.left + this.top = other.top + this.width = other.width + this.height = other.height + } + + public set(x: number, y: number, w: number, h: number) { + this.left = x + this.top = y + this.width = w + this.height = h + } + + public static Grow(box: Bounds, amount = 1): Bounds { + const b = box.clone() + b.grow(amount) + return b + } + + public static GrowXY(box: Bounds, x: number, y: number): Bounds { + const b = box.clone() + b.growxy(x, y) + return b + } + + public grow(amount = 1): this { + this.top -= amount + this.left -= amount + this.width += amount * 2 + this.height += amount * 2 + return this + } + + public growxy(x: number, y: number): this { + this.top -= x + this.left -= y + this.width += x * 2 + this.height += y * 2 + return this + } + + public static Translate(box: Bounds, p: Vec2): Bounds { + return new Bounds({ + left: box.left + p.x, + top: box.top + p.y, + width: box.width, + height: box.height, + }) + } + + public translate(p: Vec2): this { + this.left += p.x + this.top += p.y + return this + } + + public static Intersects(a: Bounds, b: Bounds): boolean { + if (b.contains(a.topLeft)) { + return true + } + if (b.contains(a.topRight)) { + return true + } + if (b.contains(a.bottomLeft)) { + return true + } + if (b.contains(a.bottomRight)) { + return true + } + if (a.contains(b.topLeft)) { + return true + } + if (a.contains(b.topRight)) { + return true + } + if (a.contains(b.bottomLeft)) { + return true + } + if (a.contains(b.bottomRight)) { + return true + } + return false + } + + public contains(p: Vec2): boolean { + return ( + p.x >= this.left && + p.x <= this.right && + p.y >= this.top && + p.y <= this.bottom + ) + } + + public add(other: Bounds): this { + this.left = Math.min(this.left, other.left) + this.top = Math.min(this.top, other.top) + this.right = Math.max(this.right, other.right) + this.bottom = Math.max(this.bottom, other.bottom) + return this + } + + public static FromImage(i: SImage): Bounds { + let left = i.width + let top = i.height + let right = 0 + let bottom = 0 + + for (let c = 0; c < i.width; c++) { + for (let r = 0; r < i.height; r++) { + if (i.getPixel(c, r)) { + left = Math.min(left, c) + top = Math.min(top, r) + right = Math.max(right, c) + bottom = Math.max(bottom, r) + } + } + } + + const width = right - left + const height = bottom - top + + return new Bounds({ width, height, left, top }) + } + + public static FromSprite(k: Sprite): Bounds { + return Bounds.FromImage(k.image) + .translate(new Vec2(-(k.width >> 1), -(k.height >> 1))) + .translate(k.xfrm.worldPos) + } + + public drawRect(color: number) { + const top = this.top + const left = this.left + const right = this.right + const bottom = this.bottom + Screen.drawLine(left, top, right, top, color) + Screen.drawLine(left, bottom, right, bottom, color) + Screen.drawLine(left, top, left, bottom, color) + Screen.drawLine(right, top, right, bottom, color) + } + + public fillRect(color: number) { + Screen.fillRect(this.left, this.top, this.width, this.height, color) + } + + public toString() { + return `Bounds(l:${this.left},t:${this.top},w:${this.width},h:${this.height},r:${this.right},b:${this.bottom})` + } + } + + export class Occlusions { + public get has(): boolean { + return !!this.left || !!this.top || !!this.right || !!this.bottom + } + + constructor( + public left: number, + public top: number, + public right: number, + public bottom: number + ) {} + + public static FromSprite(s: Sprite, bounds: Bounds): Occlusions { + const w = s.xfrm.worldPos + const left = w.x - (s.width >> 1) + const top = w.y - (s.height >> 1) + const right = w.x + (s.width >> 1) + const bottom = w.y + (s.height >> 1) + return new Occlusions( + bounds.left > left ? bounds.left - left : 0, + bounds.top > top ? bounds.top - top : 0, + bounds.right < right ? right - bounds.right : 0, + bounds.bottom < bottom ? bottom - bounds.bottom : 0 + ) + } + } +} diff --git a/libs/microcode/button.ts b/libs/microcode/button.ts new file mode 100644 index 00000000000..80935979abc --- /dev/null +++ b/libs/microcode/button.ts @@ -0,0 +1,271 @@ +namespace microcode { + export class Borders { + constructor( + public top: number, + public bottom: number, + public left: number, + public right: number + ) {} + } + + export class ButtonStyle { + constructor( + public fill: number, + public borders: Borders, + public shadow: boolean + ) {} + } + + export namespace ButtonStyles { + export const ShadowedWhite = new ButtonStyle( + 1, + new Borders(1, 12, 1, 1), + true + ) + export const LightShadowedWhite = new ButtonStyle( + 1, + new Borders(1, 11, 1, 1), + true + ) + export const FlatWhite = new ButtonStyle( + 1, + new Borders(1, 1, 1, 1), + false + ) + /* + export const RectangleWhite = new ButtonStyle( + 1, + new Borders(0, 0, 0, 0), + false + ) + */ + export const BorderedPurple = new ButtonStyle( + 11, + new Borders(12, 12, 12, 12), + false + ) + export const RedBorderedWhite = new ButtonStyle( + 1, + new Borders(2, 2, 2, 2), + false + ) + export const Transparent = new ButtonStyle( + 0, + new Borders(0, 0, 0, 0), + false + ) + } + + export function borderLeft(style: ButtonStyle) { + return style.borders.left ? 1 : 0 + } + + export function borderTop(style: ButtonStyle) { + return style.borders.top ? 1 : 0 + } + + export function borderRight(style: ButtonStyle) { + return style.borders.right ? 1 : 0 + } + + export function borderBottom(style: ButtonStyle) { + return style.borders.bottom ? 1 : 0 + } + + export function borderWidth(style: ButtonStyle) { + return borderLeft(style) + borderRight(style) + } + + export function borderHeight(style: ButtonStyle) { + return borderTop(style) + borderBottom(style) + } + + export class ButtonBase implements IComponent, ISizable, IPlaceable { + public icon: Sprite + private xfrm_: Affine + private style: ButtonStyle + + constructor(x: number, y: number, style: ButtonStyle, parent: Affine) { + this.xfrm_ = new Affine() + this.xfrm.localPos.x = x + this.xfrm.localPos.y = y + this.style = style + this.xfrm.parent = parent + } + + public get xfrm() { + return this.xfrm_ + } + public get width() { + return this.bounds.width + } + public get height() { + return this.bounds.height + } + + public get bounds() { + // Returns bounds in local space + return Bounds.GrowXY( + this.icon.bounds, + borderLeft(this.style), + borderTop(this.style) + ) + } + + public get rootXfrm(): Affine { + let xfrm = this.xfrm + while (xfrm.parent) { + xfrm = xfrm.parent + } + return xfrm + } + + public buildSprite(img: SImage) { + this.icon = new Sprite({ + parent: this, + img, + }) + this.icon.xfrm.parent = this.xfrm + } + + public getImage() { + return this.icon.image + } + + public occlusions(bounds: Bounds) { + return this.icon.occlusions(bounds) + } + + public setVisible(visible: boolean) { + this.icon.invisible = !visible + if (!visible) { + this.hover(false) + } + } + + public visible() { + return !this.icon.invisible + } + + public hover(hov: boolean) {} + public update() {} + + isOffScreenX(): boolean { + return this.icon.isOffScreenX() + } + + draw() { + this.drawStyle() + this.drawIcon() + } + + private drawIcon() { + this.icon.draw() + } + + private drawStyle() { + if (this.style.fill) + Screen.fillBoundsXfrm( + this.xfrm, + this.icon.bounds, + this.style.fill + ) + if (this.style.borders) + Screen.outlineBoundsXfrm4( + this.xfrm, + this.icon.bounds, + 1, + this.style.borders + ) + if (this.style.shadow) { + Screen.setPixelXfrm( + this.xfrm, + this.icon.bounds.left - 1, + this.icon.bounds.bottom, + this.style.borders.bottom + ) + Screen.setPixelXfrm( + this.xfrm, + this.icon.bounds.right + 1, + this.icon.bounds.bottom, + this.style.borders.bottom + ) + } + } + } + + export class Button extends ButtonBase { + private iconId: string | SImage + private _ariaId: string + public onClick?: (button: Button) => void + + public get ariaId(): string { + return ( + this._ariaId || + (typeof this.iconId === "string" ? this.iconId : "") + ) + } + + public set ariaId(value: string) { + this._ariaId = value + } + + reportAria(force = false) { + const msg: accessibility.TileAccessibilityMessage = { + type: "tile", + value: this.ariaId, + force, + } + accessibility.setLiveContent(msg) + } + + constructor(opts: { + parent?: IPlaceable + style?: ButtonStyle + icon: string | SImage + ariaId?: string + x: number + y: number + onClick?: (button: Button) => void + }) { + super( + opts.x, + opts.y, + opts.style || ButtonStyles.Transparent, + opts.parent && opts.parent.xfrm + ) + this.iconId = opts.icon + this._ariaId = opts.ariaId + this.onClick = opts.onClick + this.buildSprite(this.image_()) + } + + public getIcon() { + return this.iconId + } + + private image_() { + return typeof this.iconId == "string" + ? icons.get(this.iconId) + : this.iconId + } + public setIcon(iconId: string, img?: SImage) { + this.iconId = iconId + if (img) this.icon.setImage(img) + else this.buildSprite(this.image_()) + } + + public clickable() { + return this.visible() && this.onClick != null + } + + public click() { + if (!this.visible()) { + return + } + if (this.onClick) { + this.onClick(this) + } + } + } +} diff --git a/libs/microcode/component.ts b/libs/microcode/component.ts new file mode 100644 index 00000000000..0ad83fd7daf --- /dev/null +++ b/libs/microcode/component.ts @@ -0,0 +1,28 @@ +namespace microcode { + + export interface IComponent { + update: () => void + draw: () => void + } + + export interface IPlaceable { + xfrm: Affine + } + + export interface ISizable { + width: number + height: number + } + + export class Placeable implements IPlaceable { + private xfrm_: Affine + //% blockCombine block="xfrm" callInDebugger + public get xfrm() { + return this.xfrm_ + } + constructor(parent?: IPlaceable) { + this.xfrm_ = new Affine() + this.xfrm_.parent = parent && parent.xfrm + } + } +} diff --git a/libs/microcode/config.ts b/libs/microcode/config.ts new file mode 100644 index 00000000000..c35ec484c10 --- /dev/null +++ b/libs/microcode/config.ts @@ -0,0 +1,14 @@ +namespace userconfig { + export const DISPLAY_CFG0 = 0x02000080 // don't wait for shield on startup + // TODO this should be only enabled on micro:bit + + // doubled screen size on browser + export const ARCADE_SCREEN_WIDTH = 240 + export const ARCADE_SCREEN_HEIGHT = 180 +} + +namespace microcode { + export const ERROR_NOT_INTEGER = 101 + export const ERROR_DOUBLE_BACKGROUND_CAPTURE = 102 + export const CAR_TILES = true +} diff --git a/libs/microcode/cursor.ts b/libs/microcode/cursor.ts new file mode 100644 index 00000000000..2964d2ed7f5 --- /dev/null +++ b/libs/microcode/cursor.ts @@ -0,0 +1,146 @@ +namespace microcode { + export type CursorCancelHandler = () => void + + export enum CursorDir { + Up, + Down, + Left, + Right, + Back, + } + + export interface CursorState { + navigator: INavigator + pos: Vec2 + ariaId: string + size: Bounds + } + + export class Cursor implements IComponent, IPlaceable { + xfrm: Affine + navigator: INavigator + cancelHandlerStack: CursorCancelHandler[] + moveStartMs: number + moveDest: Vec2 + ariaPos: Vec2 + ariaId: string + size: Bounds + visible = true + + constructor() { + this.xfrm = new Affine() + this.cancelHandlerStack = [] + this.moveDest = new Vec2() + this.setSize() + } + + public moveTo(pos: Vec2, ariaId: string, sizeHint: Bounds) { + this.setSize(sizeHint) + this.moveDest.copyFrom(pos) + this.moveStartMs = control.millis() + this.setAriaContent(ariaId) + } + + public setAriaContent(ariaId: string, ariaPos: Vec2 = null) { + this.ariaId = ariaId || "" + this.ariaPos = ariaPos + } + + public snapTo(x: number, y: number, ariaId: string, sizeHint: Bounds) { + this.setSize( + sizeHint || + new Bounds({ left: 0, top: 0, width: 16, height: 16 }) + ) + this.moveDest.x = this.xfrm.localPos.x = x + this.moveDest.y = this.xfrm.localPos.y = y + this.setAriaContent(ariaId) + } + + public setSize(size?: Bounds) { + size = + size || new Bounds({ left: 0, top: 0, width: 16, height: 16 }) + if (this.size) this.size.copyFrom(size) + else this.size = size.clone() + } + + public saveState(): CursorState { + return { + navigator: this.navigator, + pos: this.xfrm.localPos.clone(), + ariaId: this.ariaId, + size: this.size.clone(), + } + } + + public restoreState(state: CursorState) { + this.navigator = state.navigator + this.xfrm.localPos.copyFrom(state.pos) + this.moveDest.copyFrom(state.pos) + this.ariaId = state.ariaId + this.size.copyFrom(state.size) + } + + public move(dir: CursorDir): Button { + return this.navigator.move(dir) + } + + public click(): boolean { + let target = this.navigator.getCurrent() //.sort((a, b) => a.z - b.z); + if (target) { + target.click() + profile() + return true + } + return false + } + + public cancel(): boolean { + if (this.cancelHandlerStack.length) { + this.cancelHandlerStack[this.cancelHandlerStack.length - 1]() + return true + } + return false + } + + update() { + this.xfrm.localPos.copyFrom(this.moveDest) + } + + draw() { + control.enablePerfCounter() + if (!this.visible) return + + Screen.outlineBoundsXfrm( + this.xfrm, + this.size, + 1, + 6 + ) + Screen.outlineBoundsXfrm( + this.xfrm, + this.size, + 2, + 9 + ) + + const text = accessibility.ariaToTooltip(this.ariaId) + if (text) { + const pos = this.ariaPos || this.xfrm.localPos + const n = text.length + const font = microcode.font + const w = font.charWidth * n + const h = font.charHeight + const x = Math.max( + Screen.LEFT_EDGE + 1, + Math.min(Screen.RIGHT_EDGE - 1 - w, pos.x - (w >> 1)) + ) + const y = Math.min( + pos.y + (this.size.width >> 1) + (font.charHeight >> 1) + 1, + Screen.BOTTOM_EDGE - 1 - font.charHeight + ) + Screen.fillRect(x - 1, y - 1, w + 1, h + 2, 15) + Screen.print(text, x, y, 1, font) + } + } + } +} diff --git a/libs/microcode/cursorscene.ts b/libs/microcode/cursorscene.ts new file mode 100644 index 00000000000..08ea4d00040 --- /dev/null +++ b/libs/microcode/cursorscene.ts @@ -0,0 +1,135 @@ +namespace microcode { + export class CursorScene extends Scene { + navigator: INavigator + public cursor: Cursor + public picker: Picker + + constructor(app: App) { + super(app, "scene") + this.color = 11 + } + + protected moveCursor(dir: CursorDir) { + try { + this.moveTo(this.cursor.move(dir)) + } catch (e) { + if (dir === CursorDir.Up && e.kind === BACK_BUTTON_ERROR_KIND) + this.back() + else if ( + dir === CursorDir.Down && + e.kind === FORWARD_BUTTON_ERROR_KIND + ) + return + else throw e + } + } + + protected moveTo(target: Button) { + if (!target) return + this.cursor.moveTo( + target.xfrm.worldPos, + target.ariaId, + target.bounds + ) + } + + /* override */ startup() { + super.startup() + control.onEvent( + ControllerButtonEvent.Pressed, + controller.right.id, + () => this.moveCursor(CursorDir.Right) + ) + control.onEvent( + ControllerButtonEvent.Pressed, + controller.up.id, + () => this.moveCursor(CursorDir.Up) + ) + control.onEvent( + ControllerButtonEvent.Pressed, + controller.down.id, + () => this.moveCursor(CursorDir.Down) + ) + control.onEvent( + ControllerButtonEvent.Pressed, + controller.left.id, + () => this.moveCursor(CursorDir.Left) + ) + + // click + const click = () => this.cursor.click() + control.onEvent( + ControllerButtonEvent.Pressed, + controller.A.id, + click + ) + control.onEvent( + ControllerButtonEvent.Pressed, + controller.A.id + keymap.PLAYER_OFFSET, + click + ) + control.onEvent( + ControllerButtonEvent.Pressed, + controller.B.id, + () => this.back() + ) + + this.cursor = new Cursor() + this.picker = new Picker(this.cursor) + this.navigator = new RowNavigator() + this.cursor.navigator = this.navigator + } + + back() { + if (!this.cursor.cancel()) this.moveCursor(CursorDir.Back) + } + + protected handleClick(x: number, y: number) { + const target = this.cursor.navigator.screenToButton( + x - Screen.HALF_WIDTH, + y - Screen.HALF_HEIGHT + ) + if (target) { + this.moveTo(target) + target.click() + } else if (this.picker.visible) { + this.picker.hide() + } + } + + protected handleMove(x: number, y: number) { + const btn = this.cursor.navigator.screenToButton( + x - Screen.HALF_WIDTH, + y - Screen.HALF_HEIGHT + ) + if (btn) { + const w = btn.xfrm.worldPos + this.cursor.snapTo(w.x, w.y, btn.ariaId, btn.bounds) + btn.reportAria(true) + } + } + + /* override */ shutdown() { + this.navigator.clear() + } + + /* override */ activate() { + super.activate() + const btn = this.navigator.initialCursor(0, 0) + if (btn) { + const w = btn.xfrm.worldPos + this.cursor.snapTo(w.x, w.y, btn.ariaId, btn.bounds) + btn.reportAria(true) + } + } + + /* override */ update() { + this.cursor.update() + } + + /* override */ draw() { + this.picker.draw() + this.cursor.draw() + } + } +} diff --git a/libs/microcode/editor.ts b/libs/microcode/editor.ts new file mode 100644 index 00000000000..9d98c096295 --- /dev/null +++ b/libs/microcode/editor.ts @@ -0,0 +1,630 @@ +namespace microcode { + const TOOLBAR_HEIGHT = 17 + const TOOLBAR_MARGIN = 2 + + //% shim=TD_NOOP + function connectJacdac() { + const buf = Buffer.fromUTF8(JSON.stringify({ type: "connect" })) + control.simmessages.send("usb", buf) + } + + //% shim=TD_NOOP + function editorSkipBack(editor: Editor, skipBack: boolean) { + if (!skipBack) editor.back() + } + + //% shim=TD_NOOP + function editorSkipForward(editor: Editor, skipBack: boolean) { + if (!skipBack) editor.forward() + } + + export function diskSlots() { + return ["disk1", "disk2", "disk3"] + } + + export class Editor extends Scene { + navigator: RuleRowNavigator + private progdef: ProgramDefn + private currPage: number + private diskBtn: Button + private connectBtn: Button + private pageBtn: Button + public pageEditor: PageEditor + public cursor: Cursor + private _changed: boolean + private hudroot: Placeable + private scrollroot: Placeable + public picker: Picker + public rendering = false + private dirty = false + + constructor(app: App) { + super(app, "editor") + this.color = 6 + } + + public changed() { + this._changed = true + } + + public nonEmptyPages(): number[] { + return this.progdef.pages + .map((p, i) => + p.rules.length > 1 || + (p.rules.length === 1 && !p.rules[0].isEmpty()) + ? i + : -1 + ) + .filter(i => i > -1) + } + + public ruleWidth() { + let w = 0 + const rules = this.pageEditor.ruleEditors + for (const rule of rules) { + w = Math.max(w, rule.innerWidth) + } + return w + 24 + } + + public pageHeight() { + const rules = this.pageEditor.ruleEditors + return ( + TOOLBAR_HEIGHT + + TOOLBAR_MARGIN + + PageEditor.MARGIN + + PageEditor.RULE_MARGIN * (rules.length - 1) + + icondb.rule_arrow.height * rules.length + ) + } + + public renderPage(p: number) { + this.switchToPage(p) + this.update() + this.dirty = true + this.draw() + } + + public saveAndCompileProgram() { + this.app.save(SAVESLOT_AUTO, this.progdef) + } + + private pickDiskSLot() { + const btns: PickerButtonDef[] = diskSlots().map(slot => { + return { + icon: slot, + } + }) + this.picker.setGroup(btns) + this.picker.show({ + title: accessibility.ariaToTooltip("disk"), + onClick: index => { + this.app.save(btns[index].icon, this.progdef) + }, + }) + } + + private pickPage() { + const btns: PickerButtonDef[] = PAGE_IDS().map(pageId => { + return { + icon: getIcon(pageId) as string, + } + }) + this.picker.setGroup(btns) + this.picker.show({ + onClick: index => { + this.switchToPage(index) + }, + }) + } + + public switchToPage(index: number, startRow = 1, startCol = 1) { + if (index < 0 || index >= this.progdef.pages.length) { + return + } + this.currPage = index + this.pageBtn.setIcon(getIcon(PAGE_IDS()[this.currPage]) as string) + this.pageEditor = new PageEditor( + this, + this.scrollroot, + this.progdef.pages[this.currPage] + ) + this.scrollroot.xfrm.localPos = new Vec2( + Screen.LEFT_EDGE, + Screen.TOP_EDGE + TOOLBAR_HEIGHT + 2 + ) + this.rebuildNavigator() + this.snapCursorTo(this.navigator.initialCursor(startRow, startCol)) + } + + public snapCursorTo(btn: Button) { + const w = btn.xfrm.worldPos + this.cursor.snapTo(w.x, w.y, btn.ariaId, btn.bounds) + btn.reportAria(true) + this.dirty = true + } + + public hoverCursorTo(btn: Button) { + const w = btn.xfrm.worldPos + this.cursor.snapTo(w.x, w.y, btn.ariaId, btn.bounds) + btn.reportAria(false) + this.dirty = true + } + + private moveTo(target: Button) { + if (target) { + this.cursor.moveTo( + target.xfrm.worldPos, + target.ariaId, + target.bounds + ) + this.dirty = true + } + } + private scrollAndMove(dir: CursorDir, skipBack = false) { + try { + const target = this.cursor.move(dir) + this.scrollAndMoveButton(target) + } catch (e) { + if (dir === CursorDir.Up && e.kind === BACK_BUTTON_ERROR_KIND) { + editorSkipBack(this, skipBack) + } else if ( + dir == CursorDir.Down && + e.kind == FORWARD_BUTTON_ERROR_KIND + ) { + editorSkipForward(this, skipBack) + } else throw e + } + } + + private scrollAndMoveButton(target: Button) { + if (!target) { + return + } + + if (target.xfrm.root === this.hudroot.xfrm) { + this.moveTo(target) + return + } + + const occBounds = new Bounds({ + left: Screen.LEFT_EDGE, + top: Screen.TOP_EDGE + TOOLBAR_HEIGHT + TOOLBAR_MARGIN, + width: Screen.WIDTH, + height: Screen.HEIGHT - (TOOLBAR_HEIGHT + TOOLBAR_MARGIN), + }) + const occ = target.occlusions(occBounds) + + if (occ.has && !this.picker.visible) { + // don't scroll if picker is visible + const xocc = occ.left ? occ.left : -occ.right + const yocc = occ.top ? occ.top : -occ.bottom + Vec2.TranslateToRef( + this.scrollroot.xfrm.localPos, + new Vec2(xocc, yocc), + this.scrollroot.xfrm.localPos + ) + } + this.moveTo(target) + } + + /* override */ startup() { + const makeOnEvent = (id: number, dir: CursorDir) => { + control.onEvent(ControllerButtonEvent.Pressed, id, () => + this.scrollAndMove(dir) + ) + } + + super.startup() + makeOnEvent(controller.right.id, CursorDir.Right) + makeOnEvent(controller.left.id, CursorDir.Left) + makeOnEvent(controller.up.id, CursorDir.Up) + makeOnEvent(controller.down.id, CursorDir.Down) + if (!Options.menuProfiling) + control.onEvent( + ControllerButtonEvent.Pressed, + controller.menu.id, + () => { + // go back to home screen + this.app.popScene() + this.app.pushScene(new Home(this.app)) + } + ) + this.hudroot = new Placeable() + this.hudroot.xfrm.localPos = new Vec2(0, Screen.TOP_EDGE) + this.scrollroot = new Placeable() + this.scrollroot.xfrm.localPos = new Vec2( + Screen.LEFT_EDGE, + Screen.TOP_EDGE + TOOLBAR_HEIGHT + TOOLBAR_MARGIN + ) + this.cursor = new Cursor() + this.picker = new Picker(this.cursor) + this.currPage = 0 + this.diskBtn = new Button({ + parent: this.hudroot, + style: ButtonStyles.BorderedPurple, + icon: icondb.disk, + ariaId: "disk", + x: Screen.LEFT_EDGE + 12, + y: 8, + onClick: () => this.pickDiskSLot(), + }) + this.connectBtn = new Button({ + parent: this.hudroot, + style: ButtonStyles.BorderedPurple, + icon: icondb.microbit_logo_btn, + ariaId: "connect", + x: Screen.LEFT_EDGE + 36, + y: 8, + onClick: () => connectJacdac(), + }) + this.pageBtn = new Button({ + parent: this.hudroot, + style: ButtonStyles.BorderedPurple, + icon: getIcon(PAGE_IDS()[this.currPage]), + x: Screen.RIGHT_EDGE - 12, + y: 8, + onClick: () => this.pickPage(), + }) + this.progdef = this.app.load(SAVESLOT_AUTO) + if (!this.progdef) { + // onboarding experience + // load first sample if this is the first program being loaded + this.progdef = ProgramDefn.fromBuffer( + new BufferReader(samples(true)[1].source) + ) + this.app.save(SAVESLOT_AUTO, this.progdef) + } + this.configureP1Keys() + this.configureP2Keys() + } + + private configureP1Keys() { + const forward = () => { + this.cursor.click() + this.dirty = true + } + control.onEvent( + ControllerButtonEvent.Pressed, + controller.A.id, + forward + ) + control.onEvent( + ControllerButtonEvent.Pressed, + controller.A.id + keymap.PLAYER_OFFSET, + forward + ) + control.onEvent( + ControllerButtonEvent.Pressed, + controller.B.id, + () => this.back() + ) + } + + private nextPage(startRow = 1, startCol = 1) { + this.switchToPage( + (this.currPage + 1) % this.progdef.pages.length, + startRow, + startCol + ) + } + + private prevPage(startRow = 1, startCol = 1) { + this.switchToPage( + (this.currPage + this.progdef.pages.length - 1) % + this.progdef.pages.length, + startRow, + startCol + ) + } + + private configureP2Keys() { + // P2 bindings + const nextPage = () => this.nextPage() + const prevPage = () => this.prevPage() + // page up, page down + control.onEvent( + ControllerButtonEvent.Pressed, + ControllerButton.Up + keymap.PLAYER_OFFSET, + nextPage + ) + control.onEvent( + ControllerButtonEvent.Pressed, + ControllerButton.Down + keymap.PLAYER_OFFSET, + prevPage + ) + // next, prev page + control.onEvent( + ControllerButtonEvent.Pressed, + ControllerButton.Left + keymap.PLAYER_OFFSET, + prevPage + ) + control.onEvent( + ControllerButtonEvent.Pressed, + ControllerButton.Right + keymap.PLAYER_OFFSET, + nextPage + ) + } + + back() { + if (!this.cursor.cancel()) { + if (this.navigator.getRow() == 0) { + if (this.currPage > 0) { + this.prevPage(0, -1) + } else { + this.app.popScene() + this.app.pushScene(new Home(this.app)) + // back to home screen from editor, stop jacscript by running empty program + } + } else { + if (this.navigator.atRuleStart()) { + const target = this.navigator.initialCursor(0, 0) + this.moveTo(target) + } else this.scrollAndMove(CursorDir.Back) + } + } + this.dirty = true + } + + forward() { + if (!this.picker.visible) this.nextPage(0, -1) + } + + protected handleClick(x: number, y: number) { + const target = this.cursor.navigator.screenToButton( + x - Screen.HALF_WIDTH, + y - Screen.HALF_HEIGHT + ) + if (target) { + this.snapCursorTo(target) + target.click() + } else if (this.picker.visible) { + this.picker.hide() + } + } + + protected handleMove(x: number, y: number) { + const target = this.cursor.navigator.screenToButton( + x - Screen.HALF_WIDTH, + y - Screen.HALF_HEIGHT + ) + if (target) { + this.hoverCursorTo(target) + } + } + + protected handleWheel(dx: number, dy: number) { + if (dy < 0) { + this.scrollAndMove(CursorDir.Up, true) + } else if (dy > 0) { + this.scrollAndMove(CursorDir.Down) + } + } + + /* override */ shutdown() { + this.progdef = undefined + this.navigator.clear() + } + + /* override */ activate() { + super.activate() + this.pageBtn.setIcon(tidToString(PAGE_IDS()[this.currPage])) + if (!this.pageEditor) { + this.switchToPage(this.currPage) + } + this.saveAndCompileProgram() + } + + public addButtons(btns: Button[]) { + this.navigator.addButtons(btns) + } + + private rebuildNavigator() { + if (this.picker.visible) return + + if (this.navigator) { + this.navigator.clear() + } else this.navigator = new RuleRowNavigator() + + this.navigator.addButtons( + this.connectBtn.visible() + ? [this.diskBtn, this.connectBtn, this.pageBtn] + : [this.diskBtn, this.pageBtn] + ) + + this.pageEditor.addToNavigator() + + this.cursor.navigator = this.navigator + } + + update() { + if (this.pageEditor) { + this.pageEditor.update() + } + if (this._changed) { + this._changed = false + this.rebuildNavigator() + } + // TODO: need this anymore??? + this.cursor.update() + } + + draw() { + if (this.dirty) { + Screen.image.fill(this.color) + if (!this.backgroundCaptured) { + this.drawBackground() + this.drawEditor() + this.drawNav() + } + this.picker.draw() + if (!this.rendering) this.cursor.draw() + this.dirty = false + } + } + + private drawEditor() { + + if (this.pageEditor) this.pageEditor.draw() + } + + private drawBackground() { + let x = Screen.LEFT_EDGE - (this.currPage << 4) + while (x < Screen.RIGHT_EDGE) { + Screen.drawTransparentImage( + editorBackground, + x, + Screen.TOP_EDGE + ) + x += editorBackground.width + } + } + + private drawNav() { + // if dot matrix is visible, then we're connected to some Jacdac bus + // TODO: move cursor to next button when visible? + if (!this.rendering) { + this.diskBtn.draw() + const wasVisible = this.connectBtn.visible() + this.connectBtn.setVisible( + true // jdc.numServiceInstances(jacs.ServiceClass.DotMatrix) == 0 + ) + if (wasVisible !== this.connectBtn.visible()) this.changed() + if (this.connectBtn.visible()) this.connectBtn.draw() + } + this.pageBtn.draw() + } + } + + export class PageEditor implements IComponent, IPlaceable { + private xfrm_: Affine + public ruleEditors: RuleEditor[] + + //% blockCombine block="xfrm" callInDebugger + public get xfrm() { + return this.xfrm_ + } + + constructor( + private editor: Editor, + parent: IPlaceable, + private pagedef: PageDefn + ) { + this.xfrm_ = new Affine() + this.xfrm_.parent = parent.xfrm + this.ruleEditors = pagedef.rules.map( + (ruledef, index) => new RuleEditor(editor, this, ruledef, index) + ) + this.ensureFinalEmptyRule() + this.layout() + } + + private ensureFinalEmptyRule() { + if (this.ruleEditors) { + this.trimRules() + const ruledefn = new RuleDefn() + this.ruleEditors.push( + new RuleEditor( + this.editor, + this, + ruledefn, + this.ruleEditors.length + ) + ) + this.pagedef.rules.push(ruledefn) + } + } + + private trimRules() { + if (!this.ruleEditors.length) { + return + } + let last = this.ruleEditors[this.ruleEditors.length - 1] + while (last.isEmpty()) { + this.ruleEditors.pop() + this.pagedef.rules.pop() + if (!this.ruleEditors.length) { + return + } + last = this.ruleEditors[this.ruleEditors.length - 1] + } + } + + public static MARGIN = 10 + public static RULE_MARGIN = 3 + public layout() { + if (!this.ruleEditors) return + this.ruleEditors.forEach(rule => { + rule.layout() + }) + let left = PageEditor.MARGIN + let top = PageEditor.MARGIN + this.ruleEditors.forEach((rule, index) => { + if (index) { + top += this.ruleEditors[index - 1].bounds.height >> 1 + top += rule.bounds.height >> 1 + top += PageEditor.RULE_MARGIN + } + rule.xfrm.localPos.x = left + rule.xfrm.localPos.y = top + }) + // Make all rules the same width + let maxRuleWidth = 0 + this.ruleEditors.forEach(rule => { + maxRuleWidth = Math.max(maxRuleWidth, rule.bounds.width) + }) + this.ruleEditors.forEach(rule => { + rule.bounds.width = maxRuleWidth + }) + } + + public addToNavigator() { + this.ruleEditors.forEach(rule => { + this.editor.navigator.addRule(rule.ruledef) + this.editor.addButtons(rule.getRuleButtons()) + }) + } + + public changed() { + this.ensureFinalEmptyRule() + this.layout() + this.editor.changed() + } + + public deleteRuleAt(index: number) { + const rule = this.ruleEditors[index] + this.pagedef.deleteRuleAt(index) + this.ruleEditors.splice(index, 1) + this.ruleEditors.forEach((rule, index) => (rule.index = index)) + this.changed() + this.editor.saveAndCompileProgram() + } + + public insertRuleAt(index: number) { + const newRule = this.pagedef.insertRuleAt(index) + if (newRule) { + this.editor.saveAndCompileProgram() + const rules: RuleEditor[] = [] + for (let i = 0; i < index; ++i) { + rules.push(this.ruleEditors[i]) + } + rules.push(new RuleEditor(this.editor, this, newRule, index)) + for (let i = index; i < this.ruleEditors.length; ++i) { + rules.push(this.ruleEditors[i]) + } + this.ruleEditors = rules + this.ruleEditors.forEach((rule, index) => (rule.index = index)) + this.changed() + } + } + + update() { + this.ruleEditors.forEach(rule => rule.update()) + } + + draw() { + control.enablePerfCounter() + this.ruleEditors.forEach(rule => rule.draw()) + } + } +} diff --git a/libs/microcode/fieldeditors.ts b/libs/microcode/fieldeditors.ts new file mode 100644 index 00000000000..646e656f49b --- /dev/null +++ b/libs/microcode/fieldeditors.ts @@ -0,0 +1,378 @@ +namespace microcode { + export function getFieldEditor(tile: Tile): FieldEditor { + if (tile instanceof ModifierEditor) return tile.fieldEditor + return undefined + } + + class FieldEditor { + init(): any { + return undefined + } + clone(field: any): any { + return undefined + } + editor( + field: any, + picker: Picker, + onHide: () => void, + onDelete?: () => void + ): void {} + toImage(field: any): SImage { + return undefined + } + toBuffer(field: any): Buffer { + return undefined + } + fromBuffer(buf: BufferReader): any { + return undefined + } + } + + export class ModifierEditor { + constructor(public tid: number) { + this.firstInstance = false + } + fieldEditor: FieldEditor + firstInstance: boolean + getField(): any { + return null + } + getIcon(): string | SImage { + return null + } + getNewInstance(field: any = null): ModifierEditor { + return null + } + serviceCommandArg(): Buffer { + return null + } + } + + class IconFieldEditor extends FieldEditor { + init() { + return img` + . . . . . + . 1 . 1 . + . . . . . + 1 . . . 1 + . 1 1 1 . + ` + } + clone(img: SImage) { + return img.clone() + } + editor( + field: any, + picker: Picker, + onHide: () => void, + onDelete?: () => void + ) { + iconEditor(field, picker, onHide, onDelete) + } + toImage(field: any) { + return icondb.renderMicrobitLEDs(field) + } + toBuffer(img: SImage) { + const ret = Buffer.create(4) + for (let index = 0; index < 25; index++) { + let byte = index >> 3 + let bit = index & 7 + let col = index % 5 + let row = Math.idiv(index, 5) + ret[byte] |= img.getPixel(col, row) << bit + } + return ret + } + fromBuffer(br: BufferReader) { + const buf = br.readBuffer(4) + const img = image.create(5, 5) + for (let index = 0; index < 25; index++) { + let byte = index >> 3 + let bit = index & 7 + let col = index % 5 + let row = Math.idiv(index, 5) + img.setPixel(col, row, (buf[byte] >> bit) & 1) + } + return img + } + } + + export class IconEditor extends ModifierEditor { + field: SImage + constructor(field: SImage = null) { + super(Tid.TID_MODIFIER_ICON_EDITOR) + this.fieldEditor = new IconFieldEditor() + this.field = this.fieldEditor.clone( + field ? field : this.fieldEditor.init() + ) + } + + getField() { + return this.field + } + + getIcon(): string | SImage { + return this.firstInstance + ? getIcon(Tid.TID_MODIFIER_ICON_EDITOR) + : this.fieldEditor.toImage(this.field) + } + + getNewInstance(field: any = null) { + return new IconEditor(field ? field : this.field.clone()) + } + + serviceCommandArg() { + const buf = Buffer.create(5) + for (let col = 0; col < 5; ++col) { + let v = 0 + for (let row = 0; row < 5; ++row) { + if (this.field.getPixel(col, row)) v |= 1 << row + } + buf[col] = v + } + return buf + } + } + + export interface Melody { + notes: string + tempo: number + } + + export const MELODY_LENGTH = 4 + export const NUM_NOTES = 5 + + //export const noteNames = ["C", "D", "E", "F", "G", "A", "B", "C", "D"] + + function setNote(buf: Buffer, offset: number, note: string) { + const noteToFreq: { [note: string]: number } = { + "0": 261.63, // C4 + "1": 293.66, // D4 + "2": 329.63, // E4 + "3": 349.23, // F4 + "4": 392.0, // G4 + "5": 440.0, // A4 + "6": 493.88, // B4 + "7": 523.25, // C5 + "8": 587.33, // D5 + } + + const period = 1000000 / (note !== "." ? noteToFreq[note] : 1000) + const duty = note === "." ? 0 : (period * 0.5) / 2 + const duration = 250 + buf.setNumber(NumberFormat.UInt16LE, offset + 0, period) + buf.setNumber(NumberFormat.UInt16LE, offset + 2, duty) + buf.setNumber(NumberFormat.UInt16LE, offset + 4, duration) + } + + class MelodyFieldEditor extends FieldEditor { + init() { + return { notes: `0240`, tempo: 120 } + } + clone(melody: Melody) { + return { notes: melody.notes.slice(0), tempo: melody.tempo } + } + editor( + field: any, + picker: Picker, + onHide: () => void, + onDelete?: () => void + ) { + melodyEditor(field, picker, onHide, onDelete) + } + toImage(field: any) { + return icondb.melodyToImage(field) + } + toBuffer(melody: Melody) { + const buf = Buffer.create(3) + buf.setUint8(0, melody.tempo) + // convert the melody notes into list of integers + const notes = melody.notes.split("") + // fill the buffer with the notes, 4 bits for each note + for (let i = 0; i < MELODY_LENGTH; i++) { + const byte = i >> 1 + const bit = (i & 1) << 2 + if (notes[i] != ".") { + const note = (parseInt(notes[i]) || 0) + 1 + buf.setUint8( + byte + 1, + buf.getUint8(byte + 1) | (note << bit) + ) + } + } + return buf + } + fromBuffer(br: BufferReader) { + const buf = br.readBuffer(3) + const tempo = buf[0] + let notes = "" + // read the notes from the buffer + for (let i = 0; i < MELODY_LENGTH; i++) { + const byte = i >> 1 + const bit = (i & 1) << 2 + const note = (buf[byte + 1] >> bit) & 0xf + notes += note == 0 ? "." : (note - 1).toString() + } + return { tempo, notes } + } + } + + export class MelodyEditor extends ModifierEditor { + field: Melody + constructor(field: Melody = null) { + super(Tid.TID_MODIFIER_MELODY_EDITOR) + this.firstInstance = false + this.fieldEditor = new MelodyFieldEditor() + this.field = this.fieldEditor.clone( + field ? field : this.fieldEditor.init() + ) + } + + getField() { + return this.field + } + + getIcon(): string | SImage { + return this.firstInstance + ? getIcon(Tid.TID_MODIFIER_MELODY_EDITOR) + : this.fieldEditor.toImage(this.field) + } + + getNewInstance(field: any = null) { + return new MelodyEditor( + field ? field : this.fieldEditor.clone(this.field) + ) + } + + serviceCommandArg() { + const buf = Buffer.create(6 * 8) + for (let i = 0; i < MELODY_LENGTH; i++) { + setNote(buf, i * 6, this.field.notes[i]) + } + return buf + } + } + + let iconEditorTile: ModifierEditor = undefined + let melodyEditorTile: ModifierEditor = undefined + export function getEditor(tid: Tid): ModifierEditor { + if (tid == Tid.TID_MODIFIER_ICON_EDITOR) { + if (!iconEditorTile) { + iconEditorTile = new IconEditor() + iconEditorTile.firstInstance = true + } + return iconEditorTile + } else if (tid == Tid.TID_MODIFIER_MELODY_EDITOR) { + if (!melodyEditorTile) { + melodyEditorTile = new MelodyEditor() + melodyEditorTile.firstInstance = true + } + return melodyEditorTile + } + return undefined + } + + function iconEditor( + image5x5: SImage, + picker: Picker, + onHide: () => void, + onDelete?: () => void + ) { + const getColor = (col: number, row: number) => { + return image5x5.getPixel(col, row) ? "solid_red" : "solid_black" + } + + // TODO: replace this with a function from index to colo + let defs: PickerButtonDef[] = [] + for (let row = 0; row < 5; row++) { + for (let col = 0; col < 5; col++) { + defs.push({ + icon: getColor(col, row), + }) + } + } + picker.setGroup(defs) + + const red = icons.get("solid_red") + const black = icons.get("solid_black") + + picker.show( + { + width: 5, + title: accessibility.ariaToTooltip(TID_MODIFIER_ICON_EDITOR), + onClick: (index: number) => { + let row = Math.idiv(index, 5) + let col = index % 5 + const on = image5x5.getPixel(col, row) + image5x5.setPixel(col, row, on ? 0 : 1) + defs[index].icon = getColor(col, row) + picker.draw() + }, + onHide, + onDelete, + navigator: () => new LEDNavigator(picker), + style: ButtonStyles.Transparent, + }, + false + ) + } + + function melodyEditor( + melody: Melody, + picker: Picker, + onHide: () => void, + onDelete?: () => void + ) { + const getIcon = (col: number, row: number) => { + const note_icon = + melody.notes[col] === "." + ? "note_off" + : parseInt(melody.notes[col]) === NUM_NOTES - 1 - row + ? "note_on" + : "note_off" + return note_icon + } + + let defs: PickerButtonDef[] = [] + for (let row = 0; row < NUM_NOTES; row++) { + for (let col = 0; col < MELODY_LENGTH; col++) { + defs.push({ + icon: getIcon(col, row), + }) + } + } + picker.setGroup(defs) + + picker.show( + { + width: MELODY_LENGTH, + title: accessibility.ariaToTooltip(TID_MODIFIER_MELODY_EDITOR), + onClick: index => { + let row = Math.idiv(index, MELODY_LENGTH) + let col = index % MELODY_LENGTH + if (getIcon(col, row) !== "note_on") { + const note = (NUM_NOTES - 1 - row).toString() + const buf = Buffer.create(6) + setNote(buf, 0, note) + } + melody.notes = + melody.notes.slice(0, col) + + (getIcon(col, row) === "note_on" + ? "." + : (NUM_NOTES - 1 - row).toString()) + + melody.notes.slice(col + 1) + for (row = 0; row < NUM_NOTES; row++) { + defs[row * MELODY_LENGTH + col].icon = getIcon(col, row) + } + picker.draw() + picker.navigator.updateAria() + }, + onHide, + onDelete, + navigator: () => new MelodyNavigator(picker), + style: ButtonStyles.Transparent, + }, + false + ) + } +} diff --git a/libs/microcode/gallery.ts b/libs/microcode/gallery.ts new file mode 100644 index 00000000000..e4bcd0c982f --- /dev/null +++ b/libs/microcode/gallery.ts @@ -0,0 +1,76 @@ +// screen for selecting from samples + +namespace microcode { + export class SamplesGallery extends CursorScene { + sampleButtons: Button[] + + /* override */ shutdown() { + super.shutdown() + } + + /* override */ startup() { + super.startup() + + let x = -72, + y = -55 + this.sampleButtons = [] + let rowButtons: Button[] = [] + samples(true) + .filter(sample => !!sample.icon) + .forEach(sample => { + const btn = new Button({ + parent: null, + style: ButtonStyles.Transparent, + icon: sample.icon, + ariaId: sample.ariaId, + x: x + 16, + y: y + 16, + onClick: () => { + reportEvent("samples.open", { + name: sample.label, + }) + this.app.saveBuffer(SAVESLOT_AUTO, sample.source) + this.app.popScene() + this.app.pushScene(new Editor(this.app)) + }, + }) + this.sampleButtons.push(btn) + rowButtons.push(btn) + x += 38 + if (x + 32 > 75) { + this.navigator.addButtons(rowButtons) + rowButtons = [] + y += 38 + x = -72 + } + }) + if (rowButtons.length > 0) this.navigator.addButtons(rowButtons) + } + + protected moveCursor(dir: CursorDir) { + if (dir == CursorDir.Back) { + // go back to home screen + this.app.popScene() + this.app.pushScene(new Home(this.app)) + } else { + super.moveCursor(dir) + } + } + /* override */ activate() { + super.activate() + this.color = 15 + } + + /* override */ draw() { + Screen.fillRect( + Screen.LEFT_EDGE, + Screen.TOP_EDGE, + Screen.WIDTH, + Screen.HEIGHT, + 0xc + ) + this.sampleButtons.forEach(s => s.draw()) + super.draw() + } + } +} diff --git a/libs/microcode/home.ts b/libs/microcode/home.ts new file mode 100644 index 00000000000..3f9fc19c8b3 --- /dev/null +++ b/libs/microcode/home.ts @@ -0,0 +1,152 @@ +namespace microcode { + export class Home extends CursorScene { + samplesBtn: Button + editBtn: Button + diskBtn: Button + + constructor(app: App) { + super(app) + } + + /* override */ startup() { + super.startup() + + this.editBtn = new Button({ + parent: null, + style: ButtonStyles.Transparent, + icon: "edit_program", + ariaId: "C0", + x: -50, + y: 30, + onClick: () => { + this.app.popScene() + this.app.pushScene(new Editor(this.app)) + }, + }) + + this.samplesBtn = new Button({ + parent: null, + style: ButtonStyles.Transparent, + icon: "smiley_buttons", + ariaId: "C1", + x: 0, + y: 30, + onClick: () => { + this.app.popScene() + this.app.pushScene(new SamplesGallery(this.app)) + }, + }) + + this.diskBtn = new Button({ + parent: null, + style: ButtonStyles.Transparent, + icon: "largeDisk", + ariaId: "load", + x: 50, + y: 30, + onClick: () => { + this.pickDiskSLot() + }, + }) + + const btns: Button[] = [this.editBtn, this.samplesBtn, this.diskBtn] + + this.navigator.addButtons(btns) + // handle menu? + } + + private pickDiskSLot() { + const btns: PickerButtonDef[] = diskSlots().map(slot => { + return { + icon: slot, + } + }) + this.picker.setGroup(btns) + this.picker.show({ + title: accessibility.ariaToTooltip("load"), + onClick: index => { + let buf = settings.readBuffer(btns[index].icon) + if (!buf) { + // handles case where nothing is in slot + buf = Buffer.create(6) + for (let i = 0; i < 5; ++i) buf[i] = Tid.END_OF_PAGE + buf[5] = Tid.END_OF_PROG + } + settings.writeBuffer(SAVESLOT_AUTO, buf) + this.app.popScene() + this.app.pushScene(new Editor(this.app)) + }, + }) + } + + /* override */ activate() { + super.activate() + this.color = 15 + docs.setup(this.app) + } + + private drawVersion() { + const font = image.font5 + Screen.print( + microcode.VERSION, + Screen.RIGHT_EDGE - font.charWidth * microcode.VERSION.length, + Screen.BOTTOM_EDGE - font.charHeight - 1, + 0xb, + font + ) + } + + private yOffset = -Screen.HEIGHT >> 1 + draw() { + Screen.fillRect( + Screen.LEFT_EDGE, + Screen.TOP_EDGE, + Screen.WIDTH, + Screen.HEIGHT, + 0xc + ) + this.yOffset = Math.min(0, this.yOffset + 2) + const t = control.millis() + const dy = this.yOffset == 0 ? (Math.idiv(t, 800) & 1) - 1 : 0 + const margin = 2 + const OFFSET = (Screen.HEIGHT >> 1) - wordLogo.height - margin + const y = Screen.TOP_EDGE + OFFSET + dy + Screen.drawTransparentImage( + wordLogo, + Screen.LEFT_EDGE + ((Screen.WIDTH - wordLogo.width) >> 1) + dy, + y + this.yOffset + ) + Screen.drawTransparentImage( + microbitLogo, + Screen.LEFT_EDGE + + ((Screen.WIDTH - microbitLogo.width) >> 1) + + dy, + y - wordLogo.height + this.yOffset + margin + ) + if (!this.yOffset) { + const tagline = resolveTooltip("tagline") + Screen.print( + tagline, + Screen.LEFT_EDGE + + ((Screen.WIDTH + wordLogo.width) >> 1) + + dy - + microcode.font.charWidth * tagline.length, + Screen.TOP_EDGE + + OFFSET + + wordLogo.height + + dy + + this.yOffset + + 1, + 0xb, + microcode.font + ) + } + + this.samplesBtn.draw() + this.editBtn.draw() + this.diskBtn.draw() + this.drawVersion() + super.draw() + } + } +} diff --git a/libs/microcode/jacs_topwriter.ts b/libs/microcode/jacs_topwriter.ts new file mode 100644 index 00000000000..28335a114bd --- /dev/null +++ b/libs/microcode/jacs_topwriter.ts @@ -0,0 +1,51 @@ +namespace jacs { + export let debugOut = false + + function scToName(sc: ServiceClass) { + if (sc == ServiceClass.Button) return "but" + if (sc == ServiceClass.DotMatrix) return "dot" + if (sc == ServiceClass.SoundLevel) return "snd" + if (sc == ServiceClass.Temperature) return "tmp" + if (sc == ServiceClass.SoundPlayer) return "mus" + if (sc == ServiceClass.Buzzer) return "buz" + if (sc == ServiceClass.Accelerometer) return "acc" + if (sc == ServiceClass.Radio) return "rad" + if (sc == ServiceClass.Potentiometer) return "pot" + if (sc == ServiceClass.LightLevel) return "lit" + if (sc == ServiceClass.MagneticFieldLevel) return "mag" + if (sc == ServiceClass.RotaryEncoder) return "rot" + if (sc == ServiceClass.Led) return "led" + if (sc == ServiceClass.Servo) return "srv" + return "unknown" + } + + export enum ServiceClass { + Button = 0x1473a263, + DotMatrix = 0x110d154b, + SoundLevel = 0x14ad1a5d, + Temperature = 0x1421bac7, + SoundPlayer = 0x1403d338, + Buzzer = 0x1b57b1d7, + Accelerometer = 0x1f140409, + Radio = 0x1ac986cf, + Potentiometer = 0x1f274746, + LightLevel = 0x17dc9a1c, + MagneticFieldLevel = 0x12fe180f, + RotaryEncoder = 0x10fa29c9, + Led = 0x1609d4f0, + Servo = 0x12fc9103, + } + + export const SRV_JACSCRIPT_CONDITION = 0x1196796d + export const CMD_CONDITION_FIRE = 0x80 + + export const CMD_GET_REG = 0x1000 + export const CMD_SET_REG = 0x2000 + + export const JD_REG_STREAMING_SAMPLES = 3 + export const JD_REG_INTENSITY = 1 + export const JD_REG_READING = 0x101 + + // delay on sending stuff in pipes and changing pages + export const ANTI_FREEZE_DELAY = 50 +} diff --git a/libs/microcode/json.ts b/libs/microcode/json.ts new file mode 100644 index 00000000000..19b499cab74 --- /dev/null +++ b/libs/microcode/json.ts @@ -0,0 +1,65 @@ +namespace microcode { + function ruleDefnFromJson(obj: any): RuleDefn { + const extractField = (t: string) => (s: string) => { + let hasField = s.indexOf("(") + if (hasField >= 0) { + const elem = s.substr(0, hasField) + if (Object.keys(tilesDB[t]).indexOf(elem) >= 0) { + const tile = tilesDB[t][elem] + const field = tile.fieldEditor.deserialize( + s.substr(hasField + 1, s.length - 2 - hasField) + ) + const newOne = tile.getNewInstance(field) + return newOne + } else { + return undefined + } + } else { + return Object.keys(tilesDB[t]).indexOf(s) >= 0 + ? tilesDB[t][s] + : undefined + } + } + const defn = new RuleDefn() + const parseTile = (single: string, name: string) => { + if (Array.isArray(obj[single])) { + const tiles: any[] = obj[single] + return tiles.map(extractField(name)).filter(t => !!t) + } + return [] + } + if (typeof obj === "string") { + obj = JSON.parse(obj) + } + + defn.sensors = parseTile("S", "sensors") + defn.actuators = parseTile("A", "actuators") + defn.filters = parseTile("F", "filters") + defn.modifiers = parseTile("M", "modifiers") + return defn + } + + function pageDefnFromJson(obj: any): PageDefn { + if (typeof obj === "string") { + obj = JSON.parse(obj) + } + const defn = new PageDefn() + if (Array.isArray(obj["R"])) { + const rules: any[] = obj["R"] + defn.rules = rules.map(ruleDefnFromJson) + } + return defn + } + + function progDefnFromJson(obj: any): ProgramDefn { + if (typeof obj === "string") { + obj = JSON.parse(obj) + } + const defn = new ProgramDefn() + if (obj && obj["P"] && Array.isArray(obj["P"])) { + const pages: any[] = obj["P"] + defn.pages = pages.map(pageDefnFromJson) + } + return defn + } +} diff --git a/libs/microcode/language.ts b/libs/microcode/language.ts new file mode 100644 index 00000000000..e71f5e4d618 --- /dev/null +++ b/libs/microcode/language.ts @@ -0,0 +1,360 @@ +namespace microcode { + export interface Constraints { + provides?: number[] + requires?: number[] + only?: (string | number)[] + allow?: (string | number)[] + disallow?: (string | number)[] + } + + export function mergeConstraints(src: Constraints, dst: Constraints) { + if (!src) { + return + } + if (src.provides) { + src.provides.forEach(item => dst.provides.push(item)) + } + if (src.requires) { + src.requires.forEach(item => dst.requires.push(item)) + } + if (src.only) { + src.only.forEach(item => dst.only.push(item)) + } + if (src.allow) { + src.allow.forEach(item => dst.allow.push(item)) + } + if (src.disallow) { + src.disallow.forEach(item => dst.disallow.push(item)) + } + } + + export function isCompatibleWith( + src: Constraints, + c: Constraints + ): boolean { + if (!src) return true + if (src.requires) { + let compat = false + src.requires.forEach( + req => (compat = compat || c.provides.some(pro => pro === req)) + ) + if (!compat) return false + } + return true + } + + export function filterModifierCompat( + tile: Tile, + category: string | number, + c: Constraints + ): boolean { + const tid = getTid(tile) + const only = c.only.some(cat => cat === category || cat === tid) + if (only) return true + if (c.only.length) return false + + const allows = c.allow.some(cat => cat === category || cat === tid) + if (!allows) return false + + const disallows = !c.disallow.some( + cat => cat === category || cat === tid + ) + if (!disallows) return false + + return true + } + + export type Tile = number | ModifierEditor + + export function getTid(tile: Tile): number { + if (tile instanceof ModifierEditor) return tile.tid + return tile + } + + export function getIcon(tile: Tile) { + if (tile instanceof ModifierEditor) return tile.getIcon() + return tidToString(tile) + } + + export type RuleRep = { [name: string]: Tile[] } + export class RuleDefn { + sensors: number[] + filters: number[] + actuators: number[] + modifiers: Tile[] + + constructor() { + this.sensors = [] + this.filters = [] + this.actuators = [] + this.modifiers = [] + } + + get sensor() { + if (this.sensors.length == 0) return Tid.TID_SENSOR_START_PAGE + return this.sensors[0] + } + + public getRuleRep(): RuleRep { + return { + sensors: this.sensors, + filters: this.filters, + actuators: this.actuators, + modifiers: this.modifiers, + } + } + + public isEmpty(): boolean { + return this.sensors.length === 0 && this.actuators.length === 0 + } + + public toBuffer(bw: BufferWriter) { + if (this.isEmpty()) return + bw.writeByte(this.sensor) + this.filters.forEach(filter => bw.writeByte(filter)) + this.actuators.forEach(act => bw.writeByte(act)) + this.modifiers.forEach(mod => { + bw.writeByte(getTid(mod)) + const fieldEditor = getFieldEditor(mod) + if (fieldEditor) { + bw.writeBuffer( + fieldEditor.toBuffer((mod as ModifierEditor).getField()) + ) + } + }) + } + + public static fromBuffer(br: BufferReader) { + const defn = new RuleDefn() + assert(!br.eof()) + const sensorEnum = br.readByte() + assert(isSensor(sensorEnum)) + defn.sensors.push(sensorEnum) + assert(!br.eof()) + while (isFilter(br.peekByte())) { + const filterEnum = br.readByte() + defn.filters.push(filterEnum) + assert(!br.eof()) + } + assert(!br.eof()) + if (!isActuator(br.peekByte())) { + return defn + } + assert(!br.eof()) + const actuatorEnum = br.readByte() + defn.actuators.push(actuatorEnum) + assert(!br.eof()) + while (isModifier(br.peekByte())) { + const modifierEnum = br.readByte() + const modifier = getEditor(modifierEnum) + if (modifier instanceof ModifierEditor) { + const field = modifier.fieldEditor.fromBuffer(br) + const newOne = modifier.getNewInstance(field) + defn.modifiers.push(newOne) + } else { + defn.modifiers.push(modifierEnum) + } + assert(!br.eof()) + } + return defn + } + } + + export class PageDefn { + rules: RuleDefn[] + + constructor() { + this.rules = [] + } + + public trim() { + while ( + this.rules.length && + this.rules[this.rules.length - 1].isEmpty() + ) { + this.rules.pop() + } + } + + public deleteRuleAt(index: number) { + if (index >= 0 && index < this.rules.length) { + this.rules.splice(index, 1) + } + } + + public insertRuleAt(index: number) { + if (index >= 0 && index < this.rules.length) { + const newRule = new RuleDefn() + // STS Array.splice doesn't support insert :( + // this.rules.splice(index, 0, new RuleDefn()); + const newRules: RuleDefn[] = [] + for (let i = 0; i < index; ++i) { + newRules.push(this.rules[i]) + } + newRules.push(newRule) + for (let i = index; i < this.rules.length; ++i) { + newRules.push(this.rules[i]) + } + this.rules = newRules + return newRule + } + return undefined + } + + public toBuffer(bw: BufferWriter) { + this.rules.forEach(rule => rule.toBuffer(bw)) + bw.writeByte(Tid.END_OF_PAGE) + } + + public static fromBuffer(br: BufferReader) { + const defn = new PageDefn() + assert(!br.eof()) + while (br.peekByte() != Tid.END_OF_PAGE) { + defn.rules.push(RuleDefn.fromBuffer(br)) + assert(!br.eof()) + } + br.readByte() + return defn + } + } + + export function PAGE_IDS() { + return [ + Tid.TID_MODIFIER_PAGE_1, + Tid.TID_MODIFIER_PAGE_2, + Tid.TID_MODIFIER_PAGE_3, + Tid.TID_MODIFIER_PAGE_4, + Tid.TID_MODIFIER_PAGE_5, + ] + } + + export class ProgramDefn { + pages: PageDefn[] + + constructor() { + this.pages = PAGE_IDS().map(id => new PageDefn()) + } + + public trim() { + this.pages.map(page => page.trim()) + } + + public toBuffer() { + const bw = new BufferWriter() + const magic = Buffer.create(4) + magic.setNumber(NumberFormat.UInt32LE, 0, 0x3e92f825) + bw.writeBuffer(magic) + this.pages.forEach(page => page.toBuffer(bw)) + bw.writeByte(Tid.END_OF_PROG) + console.log(`toBuffer: ${bw.length}b`) + return bw.buffer + } + + public static fromBuffer(br: BufferReader) { + const defn = new ProgramDefn() + assert(!br.eof()) + const magic = br.readBuffer(4) + if (magic.getNumber(NumberFormat.UInt32LE, 0) != 0x3e92f825) { + console.log("bad magic") + return defn + } + defn.pages = [] + assert(!br.eof()) + while (br.peekByte() != Tid.END_OF_PROG) { + defn.pages.push(PageDefn.fromBuffer(br)) + assert(!br.eof()) + } + br.readByte() + return defn + } + } + + function mkConstraints(): Constraints { + const c: Constraints = { + provides: [], + only: [], + requires: [], + allow: [], + disallow: [], + } + return c + } + + export class Language { + public static getTileSuggestions( + rule: RuleDefn, + name: string, + index: number + ): Tile[] { + // based on the name, we have a range of tiles to choose from + const [lower, upper] = ranges[name] + let all: Tile[] = [] + for (let i = lower; i <= upper; ++i) { + const ed = getEditor(i) + if (ed) all.push(ed) + else all.push(i) + } + all = all + .filter((tile: Tile) => isVisible(tile)) + .sort((t1, t2) => priority(t1) - priority(t2)) + + if (name === "sensors" || name === "actuators") return all + + // Collect existing tiles up to index. + let existing: Tile[] = [] + const ruleRep = rule.getRuleRep() + for (let i = 0; i < index; ++i) { + existing.push(ruleRep[name][i]) + } + + // Return empty set if the last existing tile is a "terminal". + if (existing.length) { + const last = existing[existing.length - 1] + if ( + isTerminal(last) || + (name === "filters" && isTerminal(rule.sensors[0])) || + (name === "modifiers" && isTerminal(rule.actuators[0])) + ) { + return [] + } + } + + // Collect the built-up constraints. + const collect = mkConstraints() + if (name === "modifiers" && rule.actuators.length) { + const src = getConstraints(rule.actuators[0]) + mergeConstraints(src, collect) + } + if (rule.sensors.length) { + const src = getConstraints(rule.sensors[0]) + mergeConstraints(src, collect) + } + + existing.forEach(tile => { + const src = getConstraints(tile) + mergeConstraints(src, collect) + }) + + return all.filter(tile => { + const src = getConstraints(tile) + const cat = getCategory(tile) + return ( + isCompatibleWith(src, collect) && + filterModifierCompat(tile, cat, collect) + ) + }) + } + + public static ensureValid(rule: RuleDefn) { + // TODO: Handle more cases. ex: + // - filters not valid for new sensor + // - modifiers not valid for new sensor or actuator + if (!rule.sensors.length) { + rule.filters = [] + } + if (!rule.actuators.length) { + rule.modifiers = [] + } + } + } +} diff --git a/libs/microcode/main.ts b/libs/microcode/main.ts new file mode 100644 index 00000000000..0257b286082 --- /dev/null +++ b/libs/microcode/main.ts @@ -0,0 +1,3 @@ +setTimeout(() => { + const app = new microcode.App() +}, 1) diff --git a/libs/microcode/math.ts b/libs/microcode/math.ts new file mode 100644 index 00000000000..c0d9add4bf1 --- /dev/null +++ b/libs/microcode/math.ts @@ -0,0 +1,143 @@ +namespace microcode { + export class Vec2 { + public get x() { + return this.x_ + } + public set x(v) { + this.x_ = v + } + public get y() { + return this.y_ + } + public set y(v) { + this.y_ = v + } + + constructor(public x_ = 0, public y_ = 0) { + // perf: ensure x_, y_ are integers + //control.assert((this.x_ | 0) == this.x_, 123) + //control.assert((this.y_ | 0) == this.y_, 123) + } + + public clone(): Vec2 { + return new Vec2(this.x, this.y) + } + + public copyFrom(v: Vec2): this { + this.x = v.x + this.y = v.y + return this + } + + public set(x: number, y: number): this { + this.x = x + this.y = y + return this + } + + public magSq(): number { + return this.x * this.x + this.y * this.y + } + + public floor(): this { + this.x = Math.floor(this.x) + this.y = Math.floor(this.y) + return this + } + + public add(v: Vec2): this { + this.x = this.x + v.x + this.y = this.y + v.y + return this + } + + public static DistSq(a: Vec2, b: Vec2): number { + const x = b.x - a.x + const y = b.y - a.y + return x * x + y * y + } + + public static ZeroToRef(ref: Vec2): Vec2 { + return ref.set(0, 0) + } + + public static TranslateToRef(v: Vec2, p: Vec2, ref: Vec2): Vec2 { + ref.x = v.x + p.x + ref.y = v.y + p.y + return ref + } + + public static ScaleToRef(v: Vec2, scale: number, ref: Vec2): Vec2 { + ref.x = v.x * scale + ref.y = v.y * scale + return ref + } + + public static FloorToRef(v: Vec2, ref: Vec2): Vec2 { + ref.x = Math.floor(v.x) + ref.y = Math.floor(v.y) + return ref + } + + public static MaxToRef(a: Vec2, b: Vec2, ref: Vec2): Vec2 { + ref.x = Math.max(a.x, b.x) + ref.y = Math.max(a.y, b.y) + return ref + } + + public static MinToRef(a: Vec2, b: Vec2, ref: Vec2): Vec2 { + ref.x = Math.min(a.x, b.x) + ref.y = Math.min(a.y, b.y) + return ref + } + + public static SubToRef(a: Vec2, b: Vec2, ref: Vec2): Vec2 { + ref.x = a.x - b.x + ref.y = a.y - b.y + return ref + } + + public static AddToRef(a: Vec2, b: Vec2, ref: Vec2): Vec2 { + ref.x = a.x + b.x + ref.y = a.y + b.y + return ref + } + + public static MulToRef(a: Vec2, b: Vec2, ref: Vec2): Vec2 { + ref.x = a.x * b.x + ref.y = a.y * b.y + return ref + } + + public static LerpToRefFix( + a: Vec2, + b: Vec2, + t: number, + ref: Vec2 + ): Vec2 { + ref.x = lerpFix(a.x, b.x, t) + ref.y = lerpFix(a.y, b.y, t) + return ref + } + + public static RandomRangeToRef( + xmin: number, + xmax: number, + ymin: number, + ymax: number, + ref: Vec2 + ): Vec2 { + ref.x = Math.randomRange(xmin, xmax) + ref.y = Math.randomRange(ymin, ymax) + return ref + } + + public toString(): string { + return `Vec2(x:${this.x},y:${this.y})` + } + } + + export function lerpFix(a: number, b: number, t: number): number { + return a + (((b - a) * t) >> 8) + } +} diff --git a/libs/microcode/navigator.ts b/libs/microcode/navigator.ts new file mode 100644 index 00000000000..9909400f4e9 --- /dev/null +++ b/libs/microcode/navigator.ts @@ -0,0 +1,401 @@ +namespace microcode { + export interface INavigator { + clear: () => void + addButtons: (btns: Button[]) => void + move: (dir: CursorDir) => Button + getCurrent: () => Button + screenToButton: (x: number, y: number) => Button + initialCursor: (row: number, col: number) => Button + updateAria: () => void + } + + export const BACK_BUTTON_ERROR_KIND = "back_button" + export const FORWARD_BUTTON_ERROR_KIND = "forward_button" + export class NavigationError { + kind: string + constructor(kind: string) { + this.kind = kind + } + } + + // ragged rows of buttons + export class RowNavigator implements INavigator { + protected buttonGroups: Button[][] + protected row: number + protected col: number + + constructor() { + this.buttonGroups = [] + } + + public clear() { + this.buttonGroups = [] + } + + public getRow() { + return this.row + } + + public addButtons(btns: Button[]) { + this.buttonGroups.push(btns) + } + + public screenToButton(x: number, y: number): Button { + const p = new Vec2(x, y) + for (let row = 0; row < this.buttonGroups.length; row++) { + const buttons = this.buttonGroups[row] + const target = buttons.find(btn => + Bounds.Translate(btn.bounds, btn.xfrm.worldPos).contains(p) + ) + if (target) { + this.row = row + this.col = buttons.indexOf(target) + return target + } + } + return undefined + } + + public move(dir: CursorDir) { + this.makeGood() + switch (dir) { + case CursorDir.Up: { + if (this.row == 0) + throw new NavigationError(BACK_BUTTON_ERROR_KIND) + this.row-- + // because the column in new row may be out of bounds + this.makeGood() + break + } + + case CursorDir.Down: { + if (this.row == this.buttonGroups.length - 1) + throw new NavigationError(FORWARD_BUTTON_ERROR_KIND) + this.row++ + // because the column in new row may be out of bounds + this.makeGood() + break + } + + case CursorDir.Left: { + if (this.col == 0) { + if (this.row > 0) { + this.row-- + } else { + this.row = this.buttonGroups.length - 1 + } + this.col = this.buttonGroups[this.row].length - 1 + } else this.col-- + break + } + + case CursorDir.Right: { + if (this.col == this.buttonGroups[this.row].length - 1) { + if (this.row < this.buttonGroups.length - 1) { + this.row++ + } else { + this.row = 0 + } + this.col = -1 + } + this.col++ + break + } + + case CursorDir.Back: { + if (this.col > 0) this.col = 0 + else if (this.row > 0) this.row-- + else return undefined + break + } + } + const btn = this.buttonGroups[this.row][this.col] + this.reportAria(btn) + return btn + } + + public updateAria() { + this.reportAria(this.getCurrent()) + } + + protected reportAria(btn: Button) { + if (btn) btn.reportAria(true) + } + + public getCurrent(): Button { + return this.buttonGroups[this.row][this.col] + } + + protected makeGood() { + if (this.row >= this.buttonGroups.length) + this.row = this.buttonGroups.length - 1 + if (this.col >= this.buttonGroups[this.row].length) + this.col = this.buttonGroups[this.row].length - 1 + } + + public initialCursor(row: number = 0, col: number = 0) { + const rows = this.buttonGroups.length + while (row < 0) row = (row + rows) % rows + const cols = this.buttonGroups[row].length + while (col < 0) col = (col + cols) % cols + this.row = row + this.col = col + return this.buttonGroups[row][col] + } + } + + // this adds accessibility for rule + export class RuleRowNavigator extends RowNavigator { + private rules: RuleDefn[] + + constructor() { + super() + this.rules = [] + } + + /* overrides */ + public clear() { + super.clear() + this.rules = [] + } + + public addRule(rule: RuleDefn) { + this.rules.push(rule) + } + + public atRuleStart() { + return this.row >= 1 && this.col == 0 + } + + protected reportAria(ret: Button) { + if (!ret) { + return + } + + let accessibilityMessage: accessibility.AccessibilityMessage + if (this.row > 0 && this.col == 0) { + const ruleDef = this.rules[this.row - 1] + + const whens = ruleDef.sensors + .concat(ruleDef.filters) + .map(s => tidToString(s)) + + const dos: string[] = ruleDef.actuators + .concat(ruleDef.modifiers.map(t => getTid(t))) + .map(s => tidToString(s)) + + accessibilityMessage = { + type: "rule", + whens, + dos, + } + } else { + accessibilityMessage = { + type: "tile", + value: (ret ? ret.ariaId : "") || "", + force: true, + } + } + accessibility.setLiveContent(accessibilityMessage) + } + } + + // mostly a matrix, except for last row, which may be ragged + // also supports delete button + // add support for aria + export class PickerNavigator implements INavigator { + protected deleteButton: Button + protected row: number + protected col: number + + constructor(private picker: Picker) {} + + private get width() { + return this.picker.width + } + private get length() { + return this.picker.group.defs.length + } + + get hasDelete() { + return !!this.deleteButton + } + + moveToIndex(index: number) { + assert(index < this.length, "index out of bounds") + this.row = Math.idiv(index, this.width) + this.col = index % this.width + this.reportAria() + return this.picker.group.getButtonAtIndex(index) + } + + private height() { + return Math.ceil(this.length / this.width) + } + + private currentRowWidth() { + assert(this.row >= 0, "row out of bounds") + return this.row < this.height() - 1 + ? this.width + : this.length - this.width * (this.height() - 1) + } + + public initialCursor(row: number = 0, col: number = 0): Button { + this.row = row + this.col = col + const btn = this.getCurrent() + if (btn) { + this.reportAria() + return undefined // TODO + } + return undefined + } + + clear() { + this.deleteButton = undefined + } + + addButtons(btns: ButtonBase[]) {} + + addDelete(btn: Button) { + this.deleteButton = btn + } + + getCurrent() { + // console.log(`row: ${this.row}, col: ${this.col}`) + if (this.row == -1) { + return this.deleteButton + } else { + const index = this.row * this.width + this.col + if (index < this.length) + return this.picker.group.getButtonAtIndex(index) + } + return undefined + } + + screenToButton(x: number, y: number): Button { + const p = new Vec2(x, y) + const btn = this.deleteButton + if ( + btn && + Bounds.Translate(btn.bounds, btn.xfrm.worldPos).contains(p) + ) + return btn + const np = this.picker.group.getButtonAtScreen(x, y) + if (np) { + this.row = np.y + this.col = np.x + if (this.col >= this.currentRowWidth()) + this.col = this.currentRowWidth() - 1 + return this.getCurrent() + } + return undefined + } + + move(dir: CursorDir) { + switch (dir) { + case CursorDir.Up: { + if (this.row == -1 || (!this.deleteButton && this.row == 0)) + throw new NavigationError(BACK_BUTTON_ERROR_KIND) + if (this.row > 0) this.row-- + else if (this.deleteButton) this.row = -1 + break + } + case CursorDir.Down: { + if (this.row < this.height() - 1) { + this.row++ + if (this.col >= this.currentRowWidth()) { + this.col = this.currentRowWidth() - 1 + } + } else throw new NavigationError(FORWARD_BUTTON_ERROR_KIND) + break + } + case CursorDir.Left: { + if (this.col > 0) this.col-- + else if (this.row > 0) { + this.row-- + this.col = this.width - 1 + } else if (this.deleteButton) { + this.row = -1 + } + break + } + case CursorDir.Right: { + if (this.row == -1) { + this.row = 0 + this.col = 0 + } else if (this.col < this.currentRowWidth() - 1) this.col++ + else if (this.row < this.height() - 1) { + this.row++ + this.col = 0 + } + break + } + } + this.reportAria() + return this.getCurrent() + } + + public updateAria() { + this.reportAria() + } + + protected reportAria() { + if (this.row == -1) { + accessibility.setLiveContent(< + accessibility.TextAccessibilityMessage + >{ + type: "text", + value: "delete_tile", + force: true, + }) + } + } + } + + // accessibility for LEDs + export class LEDNavigator extends PickerNavigator { + constructor(picker: Picker) { + super(picker) + this.row = 2 + this.col = 2 + } + protected reportAria() { + super.reportAria() + if (this.row == -1) return + const on = true // TODO: btn.getIcon() == "solid_red" + accessibility.setLiveContent(< + accessibility.LEDAccessibilityMessage + >{ + type: "led", + on, + x: this.col, + y: this.row, + force: true, + }) + } + } + + // accessibility for melody + export class MelodyNavigator extends PickerNavigator { + constructor(picker: Picker) { + super(picker) + this.row = 2 + this.col = 2 + } + protected reportAria() { + super.reportAria() + if (this.row == -1) return + const on = true // TODO btn.getIcon() === "note_on" + const index = this.hasDelete ? this.row - 1 : this.row + accessibility.setLiveContent(< + accessibility.NoteAccessibilityMessage + >{ + type: "note", + on, + index, + force: true, + }) + } + } +} diff --git a/libs/microcode/options.ts b/libs/microcode/options.ts new file mode 100644 index 00000000000..c9c0b00c437 --- /dev/null +++ b/libs/microcode/options.ts @@ -0,0 +1,14 @@ +namespace microcode { + export class Options { + public static fps = false + public static profiling = false + public static menuProfiling = false // heap-dump on MENU press + } + + export function profile() { + if (Options.profiling) { + control.heapSnapshot() + control.gc() // displays stats on hardware + } + } +} diff --git a/libs/microcode/picker.ts b/libs/microcode/picker.ts new file mode 100644 index 00000000000..72981e0c71c --- /dev/null +++ b/libs/microcode/picker.ts @@ -0,0 +1,267 @@ +namespace microcode { + // TODO: functionalize the Picker to reduce memory pressure + // 1. a function to get image from col, row + // 2. a function to set image from col, row + // 2. the number of rows and columns (supported ragged) + + export type PickerButtonDef = { + icon: string + ariaId?: string + } + + export interface IPicker { + size: number + getPickerButtonDef(index: number): PickerButtonDef + } + + // the picker group only needs to access the PickerButtonDefs list, + // which should be functionalized to reduce memory pressure, no + // need for buttons here + class PickerGroup { + public xfrm: Affine + public bounds: Bounds + private cell: Bounds + + constructor(public picker: Picker, public defs: PickerButtonDef[]) { + this.xfrm = new Affine() + this.xfrm.parent = picker.xfrm + } + + // TODO: on click + + public buttonHeight() { + return this.cell.height + } + + public getButtonAtIndex(idx: number): Button { + const def = this.defs[idx] + const btn = new Button({ + parent: this.picker, + style: this.picker.style, + icon: def.icon, + ariaId: def.ariaId, + x: 0, + y: 0, + onClick: () => { + this.picker.onButtonClicked(idx) + }, + }) + btn.xfrm.parent = this.xfrm + this.setButtonCoords(idx, btn) + return btn + } + + public getButtonAtScreen(x: number, y: number): Vec2 { + const p = new Vec2(x, y) + const b = Bounds.Translate(this.bounds, this.xfrm.worldPos) + if (!b.contains(p)) return undefined + const row = Math.idiv(y - b.top, this.cell.height) + const col = Math.idiv(x - b.left, this.cell.width) + return new Vec2(col, row) + } + + public layout(maxPerRow: number) { + // first compute bounds of biggest button + this.cell = new Bounds() + this.defs.forEach(def => { + const btn = new ButtonBase( + 0, + 0, + this.picker.style, + this.picker.xfrm + ) + btn.buildSprite(icons.get(def.icon)) + this.cell.add(btn.bounds) + }) + this.layoutDraw() + } + + private setButtonCoords(idx: number, btn: ButtonBase) { + btn.buildSprite(icons.get(this.defs[idx].icon)) + const row = Math.idiv(idx, this.picker.width) + btn.xfrm.localPos.x = + (this.cell.width >> 1) + + (idx % this.picker.width) * this.cell.width + + (idx % this.picker.width) + btn.xfrm.localPos.y = row * this.cell.height + } + + private layoutDraw(draw: boolean = false) { + // matrix layout of buttons + this.bounds = new Bounds() + this.defs.forEach((def, idx) => { + const btn = new ButtonBase(0, 0, this.picker.style, this.xfrm) + this.setButtonCoords(idx, btn) + this.bounds.add(Bounds.Translate(btn.bounds, btn.xfrm.localPos)) + if (draw) btn.draw() + }) + } + public draw() { + this.layoutDraw(true) + } + } + + export class Picker implements IPlaceable { + public group: PickerGroup + private start: number + public navigator: PickerNavigator + public visible: boolean + public style: ButtonStyle + public width: number + + private xfrm_: Affine + private prevState: CursorState + private deleteBtn: Button + private panel: Bounds + private onClick: (index: number) => void + private onHide: () => void + private onDelete: () => void + private hideOnClick: boolean + private title: string + + public get xfrm() { + return this.xfrm_ + } + + constructor(private cursor: Cursor) { + this.xfrm_ = new Affine() + this.group = undefined + this.navigator = new PickerNavigator(this) + } + + public setGroup(defs: PickerButtonDef[]) { + this.group = new PickerGroup(this, defs) + } + + public onButtonClicked(index: number) { + const onClick = this.onClick + if (this.hideOnClick) { + this.cursor.cancelHandlerStack.pop() + this.hide() + } + if (onClick) { + onClick(index) + } + } + + private cancelClicked() { + this.cursor.cancelHandlerStack.pop() + this.hide() + } + + show( + opts: { + width?: number + title?: string + onClick?: (index: number) => void + onHide?: () => void + onDelete?: () => void + navigator?: () => PickerNavigator + selected?: number + style?: ButtonStyle + }, + hideOnClick: boolean = true + ) { + this.start = opts.selected ? opts.selected : -1 + this.onClick = opts.onClick + this.onHide = opts.onHide + this.onDelete = opts.onDelete + if (opts.navigator) { + this.navigator.clear() + this.navigator = opts.navigator() + } else { + this.navigator.clear() + this.navigator = new PickerNavigator(this) + } + this.hideOnClick = hideOnClick + this.title = opts.title + this.style = opts.style || ButtonStyles.LightShadowedWhite + this.width = opts.width || 5 + this.prevState = this.cursor.saveState() + this.cursor.navigator = this.navigator + this.cursor.cancelHandlerStack.push(() => this.cancelClicked()) + if (this.onDelete) { + this.deleteBtn = new Button({ + parent: this, + style: ButtonStyles.RedBorderedWhite, + icon: "delete", + x: 0, + y: 0, + onClick: () => { + this.hide() + this.onDelete() + }, + }) + } + this.layout(this.width) + this.visible = true + } + + hide() { + this.visible = false + this.navigator.clear() + this.cursor.restoreState(this.prevState) + this.deleteBtn = undefined + this.group = undefined + if (this.onHide) { + this.onHide() + } + } + + draw() { + control.enablePerfCounter() + if (!this.visible) return + Screen.fillBoundsXfrm(this.xfrm, this.panel, 12) + Screen.outlineBoundsXfrm(this.xfrm, this.panel, 1, 15) + if (this.title) { + const w = this.xfrm.worldPos + Screen.print( + this.title, + w.x + this.panel.left + 2, + w.y + this.panel.top + 4, + 1, + microcode.font + ) + } + if (this.group) this.group.draw() + if (this.deleteBtn) this.deleteBtn.draw() + } + + private layout(maxPerRow: number) { + this.panel = new Bounds() + const padding = 2 + let top = padding + if (this.deleteBtn || this.title) { + top += this.deleteBtn ? this.deleteBtn.height : HEADER + } + if (this.deleteBtn) { + this.navigator.addDelete(this.deleteBtn) + } + if (this.group) { + const group = this.group + group.layout(maxPerRow) + top += group.buttonHeight() >> 1 + group.xfrm.localPos.y = top + this.panel.add(Bounds.Translate(group.bounds, new Vec2(0, top))) + top += group.bounds.height + } + + if (this.deleteBtn) { + this.deleteBtn.xfrm.localPos.x = + this.panel.right - (this.deleteBtn.width >> 1) + 1 + this.deleteBtn.xfrm.localPos.y = + this.panel.top + (this.deleteBtn.height >> 1) + } + + this.panel.grow(padding) + this.xfrm.localPos.x = padding - (this.panel.width >> 1) + this.xfrm.localPos.y = padding - (this.panel.height >> 1) + + if (this.start < 0) this.start = 0 + const btn = this.navigator.moveToIndex(this.start) + this.cursor.moveTo(btn.xfrm.worldPos, btn.ariaId, btn.bounds) + } + } + + const HEADER = 16 +} diff --git a/libs/microcode/pointerevents.ts b/libs/microcode/pointerevents.ts new file mode 100644 index 00000000000..8d92597403a --- /dev/null +++ b/libs/microcode/pointerevents.ts @@ -0,0 +1,56 @@ +namespace pointerevents { + export interface PointerEventMessage { + type: "pointerdown" | "pointerup" | "pointermove" + x: number + y: number + buttons: number + } + + export interface WheelEventMessage { + type: "wheel" + dx: number + dy: number + dz: number + } + + const contexts: { + click: (x: number, y: number) => void + move: (x: number, y: number) => void + wheel: (dx: number, dy: number) => void + }[] = [] + //% shim=TD_NOOP + export function pushContext( + click: (x: number, y: number) => void, + move: (x: number, y: number) => void, + wheel: (dx: number, dy: number) => void + ) { + contexts.push({ click, move, wheel }) + setup() + } + + //% shim=TD_NOOP + export function popContext() { + contexts.pop() + } + + //% shim=TD_NOOP + function setup() { + control.simmessages.onReceived("pointer-events", data => { + const ctx = contexts[contexts.length - 1] + if (!ctx) return + + const msg = JSON.parse(data.toString()) + // down event! + if (msg.type === "pointerdown") { + const m = msg as PointerEventMessage + ctx.click(m.x, m.y) + } else if (msg.type === "pointermove") { + const m = msg as PointerEventMessage + ctx.move(m.x, m.y) + } else if (msg.type === "wheel") { + const m = msg as WheelEventMessage + ctx.wheel(m.dx, m.dy) + } + }) + } +} diff --git a/libs/microcode/pxt.json b/libs/microcode/pxt.json new file mode 100644 index 00000000000..bdffe3a3cd7 --- /dev/null +++ b/libs/microcode/pxt.json @@ -0,0 +1,51 @@ +{ + "name": "microcode", + "version": "2.5.30", + "description": "Physical computing library for micro:bit V2", + "dependencies": { + "core": "file:../core", + "game---light": "file:../game---light" + }, + "files": [ + "analytics.ts", + "config.ts", + "main.ts", + "keymap.ts", + "accessibility.ts", + "button.ts", + "component.ts", + "assets.ts", + "scene.ts", + "language.ts", + "tiles.ts", + "app.ts", + "sprite.ts", + "editor.ts", + "ruleeditor.ts", + "cursor.ts", + "home.ts", + "bounds.ts", + "picker.ts", + "affine.ts", + "math.ts", + "screen.ts", + "binlib.ts", + "version.ts", + "fieldeditors.ts", + "navigator.ts", + "cursorscene.ts", + "options.ts", + "samples.ts", + "gallery.ts", + "pointerevents.ts", + "tooltips.ts", + "utils.ts" + ], + "testFiles": [], + "supportedTargets": [ + "microbit" + ], + "preferredEditor": "tsprj", + "disableTargetTemplateFiles": true, + "binaryonly": true +} diff --git a/libs/microcode/ruleeditor.ts b/libs/microcode/ruleeditor.ts new file mode 100644 index 00000000000..ba05937991f --- /dev/null +++ b/libs/microcode/ruleeditor.ts @@ -0,0 +1,546 @@ +namespace microcode { + type ButtonRuleRep = { [name: string]: Button[] } + + function repNames() { + return ["sensors", "filters", "actuators", "modifiers"] + } + + export class RuleEditor implements IComponent, IPlaceable { + private xfrm_: Affine + innerWidth: number + handleBtn: Button + whenInsertBtn: Button + doInsertBtn: Button + arrow: Sprite + ruleButtons: ButtonRuleRep + bounds: Bounds + whenBounds: Bounds + queuedCursorMove: CursorDir + + //% blockCombine block="xfrm" callInDebugger + public get xfrm() { + return this.xfrm_ + } + + constructor( + private editor: Editor, + private page: PageEditor, + public ruledef: RuleDefn, + public index: number + ) { + this.xfrm_ = new Affine() + this.xfrm_.parent = page.xfrm + this.handleBtn = new Button({ + parent: this, + icon: "rule_handle", + ariaId: "rule", + x: 0, + y: 0, + style: ButtonStyles.Transparent, + onClick: () => this.showRuleHandleMenu(), + }) + this.arrow = new Sprite({ + parent: this, + img: icons.get("rule_arrow"), + }) + this.ruleButtons = { + sensors: [], + filters: [], + actuators: [], + modifiers: [], + } + this.instantiateProgramTiles() + } + + private destroyWhenInsertButton() { + this.whenInsertBtn = undefined + } + + private needsWhenInsert() { + if ( + this.ruledef["sensors"].length == 0 || + this.getSuggestions("filters", this.ruledef["filters"].length) + .length + ) { + this.whenInsertBtn = new Button({ + parent: this, + style: ButtonStyles.Transparent, + icon: "when_insertion_point", + ariaId: + this.ruledef["sensors"].length == 0 + ? "when" + : undefined, + x: 0, + y: 0, + onClick: () => this.showWhenInsertMenu(), + }) + } else { + this.destroyWhenInsertButton() + } + } + + private destroyDoInsertButton() { + this.doInsertBtn = undefined + } + + private needsDoInsert() { + if ( + this.ruledef["actuators"].length == 0 || + this.getSuggestions( + "modifiers", + this.ruledef["modifiers"].length + ).length + ) { + this.doInsertBtn = new Button({ + parent: this, + style: ButtonStyles.Transparent, + icon: "do_insertion_point", + ariaId: + this.ruledef["actuators"].length == 0 + ? "do" + : undefined, + x: 0, + y: 0, + onClick: () => this.showDoInsertMenu(), + }) + } else { + this.destroyDoInsertButton() + } + } + + private destroyProgramTiles() { + let changed = false + repNames().forEach(name => { + if (this.ruleButtons[name].length) { + this.ruleButtons[name] = [] + changed = true + } + }) + // TODO: do we really need this? + if (changed) this.editor.changed() + } + + private processSection(name: string, rule: RuleRep) { + const tiles = rule[name] + tiles.forEach((tile, index) => { + const button = new Button({ + parent: this, + style: buttonStyle(tile), + icon: getIcon(tile), + ariaId: tidToString(getTid(tile)), + x: 0, + y: 0, + onClick: () => this.editTile(name, index), + }) + if (name == "filters" && index == 0) { + const sensor = this.ruledef.sensors[0] + // TODO: this logic should be part of the SensorTileDefn + if ( + (jdKind(sensor) == JdKind.Radio && + sensor != Tid.TID_SENSOR_LINE) || + jdKind(sensor) == JdKind.Variable + ) { + const plus = new Button({ + parent: this, + style: buttonStyle(tile), + icon: "arith_equals", + ariaId: "arith_equals", + x: 0, + y: 0, + }) + this.ruleButtons[name].push(plus) + } + } + this.ruleButtons[name].push(button) + if (index < tiles.length - 1) { + if ( + (jdKind(tile) == JdKind.Literal || + jdKind(tile) == JdKind.Variable) && + (jdKind(tiles[index + 1]) == JdKind.Literal || + jdKind(tiles[index + 1]) == JdKind.Variable || + jdKind(tiles[index + 1]) == JdKind.RandomToss) + ) { + const plus = new Button({ + parent: this, + style: buttonStyle(tile), + icon: "arith_plus", + ariaId: "arith_plus", + x: 0, + y: 0, + }) + this.ruleButtons[name].push(plus) + } + } + }) + return tiles.length > 0 + } + private instantiateProgramTiles() { + this.destroyProgramTiles() + const rule = this.ruledef.getRuleRep() + let changed = false + Object.keys(rule).forEach(name => { + changed = this.processSection(name, rule) || changed + }) + this.needsWhenInsert() + this.needsDoInsert() + if (changed) this.page.changed() + } + + private showRuleHandleMenu() { + const btns: PickerButtonDef[] = [ + { + icon: "plus", + ariaId: "add_rule", + }, + { + icon: "delete", + ariaId: "delete_rule", + }, + ] + this.editor.picker.setGroup(btns) + this.editor.picker.show({ + onClick: index => + this.handleRuleHandleMenuSelection( + btns[index].icon as string + ), + }) + } + + private nextEmpty(name: string, index: number) { + return ( + (name == "sensors" && + this.ruledef.filters.length == 0 && + this.whenInsertBtn) || + (name == "filters" && + index == this.ruledef.filters.length - 1 && + (this.whenInsertBtn || + this.ruledef.actuators.length == 0)) || + (name == "actuators" && + this.ruledef.modifiers.length == 0 && + this.doInsertBtn) || + (name == "modifiers" && + index == this.ruledef.modifiers.length - 1 && + this.doInsertBtn) + ) + } + + private deleteIncompatibleTiles(name: string, index: number) { + const doit = (name: string, index: number) => { + const ruleTiles = this.ruledef.getRuleRep()[name] + + while (index < ruleTiles.length) { + const suggestions = this.getSuggestions(name, index) + const compatible = suggestions.find( + t => getTid(t) == getTid(ruleTiles[index]) + ) + if (compatible) index++ + else { + ruleTiles.splice(index, ruleTiles.length - index) + return false + } + } + return true + } + doit(name, index) + if (name === "filters") { + // a change in the the when section may affect the do section + let ok = doit("actuators", 0) + if (ok) doit("modifiers", 0) + else this.ruledef.getRuleRep()["modifiers"] = [] + } + } + + private editTile(name: string, index: number) { + const ruleTiles = this.ruledef.getRuleRep()[name] + const tileUpdated = (tile: Tile) => { + const editedAdded = !!tile + if (tile) { + if (index >= ruleTiles.length) { + reportEvent("tile.add", { tid: getTid(tile) }) + ruleTiles.push(tile) + } else { + reportEvent("tile.update", { tid: getTid(tile) }) + ruleTiles[index] = tile + if (name == "sensors") + this.deleteIncompatibleTiles("filters", 0) + else if (name == "actuators") + this.deleteIncompatibleTiles("modifiers", 0) + else this.deleteIncompatibleTiles(name, index + 1) + } + } else { + ruleTiles.splice(index, 1) + reportEvent("tile.delete") + if (name == "filters" || name == "modifiers") + this.deleteIncompatibleTiles(name, index) + } + Language.ensureValid(this.ruledef) + this.editor.saveAndCompileProgram() + this.instantiateProgramTiles() + if (editedAdded && this.nextEmpty(name, index)) { + // Queue a move to the right + this.queuedCursorMove = CursorDir.Right + } + this.page.changed() + } + const newFieldEditor = (tile: ModifierEditor, del = false) => { + const newOne = del ? tile : tile.getNewInstance() + const fieldEditor = getFieldEditor(newOne) + this.editor.captureBackground() + fieldEditor.editor( + newOne.getField(), + this.editor.picker, + () => { + this.editor.releaseBackground() + tileUpdated(newOne) + }, + del + ? () => { + this.editor.releaseBackground() + tileUpdated(undefined) + } + : undefined + ) + } + if ( + index < ruleTiles.length && + ruleTiles[index] instanceof ModifierEditor + ) { + newFieldEditor(ruleTiles[index] as ModifierEditor, true) + return + } + const suggestions = this.getSuggestions(name, index) + const btns: PickerButtonDef[] = suggestions.map(tile => { + return { + icon: getIcon(tile), + } + }) + // special case for field editor + if ( + suggestions.length == 1 && + suggestions[0] instanceof ModifierEditor + ) { + let theOne = + index > 0 && ruleTiles[index - 1] instanceof ModifierEditor + ? (ruleTiles[index - 1] as ModifierEditor) + : (suggestions[0] as ModifierEditor) + newFieldEditor(theOne) + return + } + let onDelete = undefined + let selectedButton = -1 + if (index < ruleTiles.length) { + onDelete = () => { + tileUpdated(undefined) + } + const selected = btns.indexOf( + btns.find(b => b.icon === getIcon(getTid(ruleTiles[index]))) // TODO + ) + if (selected >= 0) { + selectedButton = selected + } + } + if (btns.length) { + this.editor.picker.setGroup(btns) + this.editor.picker.show({ + title: accessibility.ariaToTooltip(name), + navigator: () => new PickerNavigator(this.editor.picker), + onClick: idx => { + let theOne = suggestions[idx] + if (theOne instanceof ModifierEditor) { + // there is more work to do l + theOne = + index > 0 && + ruleTiles[index - 1] instanceof ModifierEditor + ? (ruleTiles[index - 1] as ModifierEditor) + : theOne + newFieldEditor(theOne) + } + tileUpdated(theOne) + }, + onDelete, + selected: selectedButton, + }) + } + return + } + + private handleRuleHandleMenuSelection(iconId: string) { + if (iconId === "plus") { + reportEvent("rule.add") + this.page.insertRuleAt(this.index) + } else if (iconId === "delete") { + reportEvent("rule.delete") + this.page.deleteRuleAt(this.index) + } + } + + private showWhenInsertMenu() { + if (this.ruledef.sensors.length) { + this.editTile("filters", this.ruledef.filters.length) + } else { + this.editTile("sensors", 0) + } + } + + private showDoInsertMenu() { + if (this.ruledef.actuators.length) { + this.editTile("modifiers", this.ruledef.modifiers.length) + } else { + this.editTile("actuators", 0) + } + } + + private getSuggestions(name: string, index: number) { + return Language.getTileSuggestions(this.ruledef, name, index) + } + + public getRuleButtons() { + // TODO: can this be done lazily instead? + const btns: Button[] = [] + btns.push(this.handleBtn) + this.ruleButtons.sensors.forEach(b => btns.push(b)) + this.ruleButtons.filters.forEach(b => btns.push(b)) + + if (this.whenInsertBtn) btns.push(this.whenInsertBtn) + + this.ruleButtons.actuators.forEach(b => btns.push(b)) + this.ruleButtons.modifiers.forEach(b => btns.push(b)) + + if (this.doInsertBtn) btns.push(this.doInsertBtn) + + return btns + } + + public isEmpty() { + return this.ruledef.isEmpty() + } + + update() { + if (this.queuedCursorMove) { + switch (this.queuedCursorMove) { + case CursorDir.Right: + control.raiseEvent(KEY_DOWN, controller.right.id) + break + // Add other cases as needed + } + this.queuedCursorMove = undefined + } + } + + public layout() { + const ruleRep = this.ruleButtons + const v = new Vec2() + this.whenBounds = new Bounds() + + const whenTiles = ruleRep.sensors.concat(ruleRep.filters) + const doTiles = ruleRep.actuators.concat(ruleRep.modifiers) + if (this.whenInsertBtn) whenTiles.push(this.whenInsertBtn) + if (this.doInsertBtn) doTiles.push(this.doInsertBtn) + + const firstWhenTile = whenTiles[0] + const lastWhenTile = whenTiles[whenTiles.length - 1] + + this.handleBtn.xfrm.localPos = v + v.x += this.handleBtn.width + this.whenBounds.left = v.x + + v.x += firstWhenTile.width >> 1 + v.x += 2 + + const layoutButtons = (btns: Button[]) => { + btns.forEach((btn, index) => { + if (index) { + v.x += btns[index - 1].width >> 1 + v.x += btn.width >> 1 + v.x += 1 + } + btn.xfrm.localPos = v + }) + } + + layoutButtons(whenTiles) + + v.x += lastWhenTile.bounds.width >> 1 + this.whenBounds.right = v.x + + v.x += this.arrow.bounds.width >> 1 + v.x += 1 + + this.arrow.xfrm.localPos.x = v.x + v.x += this.arrow.bounds.width + v.x += 2 + + layoutButtons(doTiles) + + if (this.doInsertBtn) { + this.doInsertBtn.xfrm.localPos = v + } + + this.bounds = undefined + + const updateSizeFromButtons = (btns: Button[]) => { + btns.forEach(btn => { + if (!this.bounds) { + this.bounds = btn.bounds + .clone() + .translate(btn.xfrm.localPos) + } else { + this.bounds.add( + Bounds.Translate(btn.bounds, btn.xfrm.localPos) + ) + } + }) + } + + updateSizeFromButtons(whenTiles) + updateSizeFromButtons(doTiles) + + if (!this.bounds) { + this.bounds = new Bounds() + } else { + this.bounds.grow(1) + } + + // Ensure that the rule "tray" is at least as wide as the screen + this.innerWidth = this.bounds.width + this.bounds.width = Math.max(this.bounds.width, Screen.WIDTH) + + this.whenBounds.left = this.bounds.left + this.whenBounds.top = this.bounds.top + this.whenBounds.height = this.bounds.height + } + + isOffScreen(): boolean { + const y = this.xfrm.worldPos.y + const b = this.bounds + return ( + y + b.top > Screen.BOTTOM_EDGE || y + b.bottom < Screen.TOP_EDGE + ) + } + + draw() { + control.enablePerfCounter() + if (this.isOffScreen()) return + + this.drawBackground() + this.handleBtn.draw() + if (this.whenInsertBtn) this.whenInsertBtn.draw() + this.arrow.draw() + if (this.doInsertBtn) this.doInsertBtn.draw() + repNames().forEach(name => { + const buttons = this.ruleButtons[name] + for (let i = 0; i < buttons.length; ++i) { + const btn = buttons[i] + if (!btn.isOffScreenX()) btn.draw() + } + }) + } + + private drawBackground() { + control.enablePerfCounter() + Screen.fillBoundsXfrm(this.xfrm, this.bounds, 11) + Screen.fillBoundsXfrm(this.xfrm, this.whenBounds, 13) + Screen.outlineBoundsXfrm(this.xfrm, this.bounds, 1, 12) + } + } +} diff --git a/libs/microcode/samples.ts b/libs/microcode/samples.ts new file mode 100644 index 00000000000..470c56452e6 --- /dev/null +++ b/libs/microcode/samples.ts @@ -0,0 +1,228 @@ +namespace microcode { + export class Sample { + constructor( + public readonly label: string, + public readonly ariaId: string, + public readonly icon: string, + public readonly b64: string + ) {} + + get source() { + return Buffer.fromBase64(this.b64) + } + } + + type rawSampleList = { + label: string + ariaId?: string + b64?: string + // leave empty to hide sample + icon?: string + }[] + + //% shim=TD_NOOP + function rawWebAppSamples(r: { s: rawSampleList }) { + r.s = r.s.concat([ + { + label: "first program", + b64: "JfiSPgtJLKBAgegAC0kpowEBAQEBAA==", + }, + { + label: "flashing heart", + ariaId: "N2", + b64: "JfiSPg4soKpGRQCgQDkCAA4powEBAQEBAA==", + icon: "flashing_heart", + }, + { + label: "counter", + ariaId: "N14", + b64: "JfiSPgtJMK2bEzOtEymlAQEBAQEA", + }, + { + label: "times table", + b64: "JfiSPg1aMbGenxQwrgtJMK2uEzOtEymlAQEBAQEA", + }, + { + label: "double counter", + b64: "JfiSPgoppAozrQtJMK2bEzOtC0oolwEKKaUKM64LSjGumxQzrgtJKJYBAQEBAA==", + }, + { + label: "pet hamster", + ariaId: "N4", + b64: "JfiSPgosoGADBwALTSygQIHoAKBgAwcAsp0LTSmjDVosoEABFwGgYAMHALKdDVoppwEBAQEBAA==", + icon: "pet_hamster", + }, + { + label: "head or tail", + ariaId: "N9", + b64: "JfiSPg0wsZwNK7N4MTWzeAEAE04soL9+5wATTyygP8b4AQEBAQEBAA==", + icon: "heads_tails", + }, + { + label: "rock, paper, scissors", + ariaId: "N8", + b64: "JfiSPg1aMLGdDVopqBNOLKAAAAAAoMA5BwATTyygAAAAAKA/xvgBE1AsoAAAAACgc5E1AQEBAQEBAA==", + icon: "rock_paper_scissors", + }, + { + label: "hot potato", + ariaId: "N7", + b64: "JfiSPg5WVVUolw4soAAQAACgAAAAAAEKLKC/fucACimnAQEBAQA=", + icon: "hot_potato", + }, + { + label: "clap lights", + ariaId: "N10", + b64: "JfiSPgosoP///wEKKaUSVyiXAQosoAAAAAAKKawSVyiWAQEBAQA=", + icon: "clap_lights", + }, + { + label: "24 7 clap", + ariaId: "N13", + b64: "JfiSPgoppQowmwosoEqprQCgjDHPALISVzCtmw5WVFQolxNSUlJSUiiYAQopowozrQ5WViiWAQoppwosoL864ACgvzoHALIOVlYolgEBAQA=", + }, + { + label: "reaction time", + ariaId: "N6", + b64: "JfiSPgosoAAIAACgABAAAKAAIAAAsg5WVVVVVVUolwtJKJkLSiiYAQosoP///wEKKaULSSiYC0oomQEKLKBEPEEAoIh4ggCyDlYolgEKLKAEfUQAoII8IgCyDlYolgEBAA==", + icon: "reaction_time", + }, + { + label: "chuck a duck", + ariaId: "N5", + b64: "JfiSPg1aLKAAEAAADVotmxFOLKDmeAcAEU4ppQEBAQEBAA==", + icon: "teleport_duck", + }, + { + label: "zombie detector", + b64: "JfiSPg4soAAQAACgQAEFAKARABABoAAAAAARTiiXC00omAEOLKCEEEAAoEopoAAOVCmmDlYolgEOLKC/OuAAoL86BwAOLZsBAQEA", + }, + { + label: "firefly", + ariaId: "N11", + b64: "JfiSPgosoAAQAAARMK2bDlMwrZsTUFIolw5UVFQolwEKLQowmwosoP/v/wEKKaUOUyiWAQEBAQA=", + icon: "firefly", + }, + { + label: "railroad crossing", + ariaId: "N12", + b64: "JfiSPgtJNZsLSS+2u7ILSjWfC0ovuLuyC00vvLIBAQEBAQA=", + icon: "railroad_crossing", + }, + { + label: "moves", + b64: "JfiSPg1cLKAnpXQADVssoCml9AANXSygIYTwAA1eLKAnnZQADVosoC889AABAQEBAQA=", + }, + { + label: "coins", + b64: "JfiSPgtJMJsLSjCtmxNQLKAecugBE1EsoC88dAATUiygvdbaAQEBAQEBAA==", + }, + { + label: "inchworm", + b64: "JfiSPgo1mw5TUyiXAQo1nw5TUyiWAQEBAQA=", + }, + { + label: "head guess", + b64: "JfiSPgoppQowsZ0KMZsNXDCxnQ1bMLGdDVsxrpsOVlZWViiXE04soOZ5BwATTyygL4TwAKAvvZQAoJ8QQgCyE1AsoCeldACgL6X0AKAvtPQAsgEKM64KKaMOVlYolgEBAQEA", + }, + { + label: "battery charger prank", + b64: "JfiSPgosoE4p5QCgTimlALINWiiXAQosoE4p5QCgTinnAKBOOecAoM455wCyDlYolgEBAQEA", + }, + { + label: "green light red light", + b64: "JfiSPgtNKJcRTyiYCiyg5VMnAKDkEwcAoPQXhwCyCimlAQowmw4trQtJMJsLSjCcE04soOVTJwCg5BMHAKD0F4cAshNPLKBRERUBAQ1aKacNWiygvzrgAKC/OgcAshFOKJYBAQEA", + }, + { + label: "crooked head or tail", + b64: "JfiSPg0wsZ0NKagTTiygv37nABNPLKA/xvgBE1AsoD/G+AEBAQEBAQA=", + }, + { + label: "step counter", + b64: "JfiSPg1aMK2bEzOtEymlAQEBAQEA", + }, + { + label: "clap counter", + b64: "JfiSPhJXMK2bEzOtAQEBAQEA", + }, + { + label: "random counter", + b64: "JfiSPgtJMJuxnwtJMRMzrQtKMa6bFF8soECB6AABAQEBAQA=", + }, + { + label: "slider levels", + b64: "JfiSPhdOM5sXTzOcF1AznRdRM54XUjOfAQEBAQEA", + }, + { + label: "light levels", + b64: "JfiSPg9OM5sPTzOcD1AznQ9RM54PUjOfAQEBAQEA", + }, + { + label: "magnet levels", + b64: "JfiSPhZOM5sWTzOcFlAznRZRM54WUjOfAQEBAQEA", + }, + { + label: "count turns", + b64: "JfiSPhhiMK2bGGMxrpsTM60UM64BAQEBAQA=", + }, + { + label: "key demo", + b64: "JfiSPgosoAAQAAALSyygQIHoAAtMLKBAARcBAQEBAQEA", + }, + { + label: "robot shake", + b64: "JfiSPgounQ1cNL4NWzTCDV00wMINXjTBwgEBAQEBAA==", + }, + { + label: "robot wake", + b64: "JfiSPgounRI0wMDAwgEBAQEBAA==", + }, + { + label: "robot avoid wall", + b64: "JfiSPgounQo0vhlPNMC+AQEBAQEA", + }, + { + label: "robot line follow", + b64: "JfiSPgoumxpoNL4aZjTAGmc0wRpqNMQaazTFGmk0wgEBAQEBAA==", + }, + { + label: "robot showcase", + b64: "JfiSPgoumwtJNL7IC0o0wsYZTzTEvgEBAQEBAA==", + }, + { + label: "robot drift tester", + b64: "JfiSPgoumwtJNL7IC0o0w8cSNMLGAQEBAQEA", + }, + ]) + } + + export function rawSamples() { + const s: rawSampleList = [ + { + label: "new program", + ariaId: "N1", + b64: "JfiSPgEBAQEBAA==", + icon: "new_program", + }, + { + label: "smiley buttons", + ariaId: "N3", + b64: "JfiSPgtJLKB7g+gAoBtEBwALSSmkC0osoHsDFwGgewPwAQtKKacBAQEBAQA=", + icon: "smiley_buttons", + }, + ] + return s + } + + export function samples(withIcon: boolean): Sample[] { + const s = rawSamples() + const r = { s: s } + rawWebAppSamples(r) + return r.s + .filter(({ icon }) => !withIcon || !!icon) + .map( + ({ label, ariaId, icon, b64 }) => + new Sample(label, ariaId, icon, b64) + ) + } +} diff --git a/libs/microcode/scene.ts b/libs/microcode/scene.ts new file mode 100644 index 00000000000..a6720361fcc --- /dev/null +++ b/libs/microcode/scene.ts @@ -0,0 +1,162 @@ +namespace microcode { + const INPUT_PRIORITY = 10 + const UPDATE_PRIORITY = 20 + const RENDER_PRIORITY = 30 + const SCREEN_PRIORITY = 100 + + export abstract class Scene implements IComponent { + private xfrm_: Affine + private color_: number + private backgroundCaptured_ = false + + //% blockCombine block="xfrm" callInDebugger + public get xfrm() { + return this.xfrm_ + } + //% blockCombine block="color" callInDebugger + public get color() { + return this.color_ + } + public set color(v) { + this.color_ = v + } + + constructor(public app: App, public name: string) { + this.xfrm_ = new Affine() + this.color_ = 12 + } + + /* abstract */ startup() { + if (Options.menuProfiling) { + control.onEvent( + ControllerButtonEvent.Pressed, + controller.menu.id, + () => { + control.heapSnapshot() + } + ) + } + } + + /* abstract */ shutdown() {} + + /* override */ activate() { + pointerevents.pushContext( + (x, y) => this.handleClick(x, y), + (x, y) => this.handleMove(x, y), + (dx, dy) => this.handleWheel(dx, dy) + ) + profile() + } + + /* override */ deactivate() { + pointerevents.popContext() + profile() + } + + /* abstract */ update() {} + + /* abstract */ draw() {} + + protected handleClick(x: number, y: number) {} + + protected handleMove(x: number, y: number) {} + + protected handleWheel(dx: number, dy: number) {} + + get backgroundCaptured() { + return !!this.backgroundCaptured_ + } + + /** + * Captures the current screen image as background image. You must call releaseBackground to resume usual rendering. + */ + captureBackground() { + control.assert( + !this.backgroundCaptured_, + ERROR_DOUBLE_BACKGROUND_CAPTURE + ) + this.backgroundCaptured_ = true + } + + releaseBackground() { + this.backgroundCaptured_ = false + } + + __init() { + control.eventContext().registerFrameHandler(INPUT_PRIORITY, () => { + control.enablePerfCounter() + const dtms = (control.eventContext().deltaTime * 1000) | 0 + controller.left.__update(dtms) + controller.right.__update(dtms) + controller.up.__update(dtms) + controller.down.__update(dtms) + }) + // Setup frame callbacks. + control.eventContext().registerFrameHandler(UPDATE_PRIORITY, () => { + control.enablePerfCounter() + this.update() + }) + control.eventContext().registerFrameHandler(RENDER_PRIORITY, () => { + control.enablePerfCounter() + // perf: render directly on the background image buffer + this.draw() + if (Options.fps) + Screen.image.print(control.EventContext.lastStats, 1, 1, 15) + if (screen !== Screen.image) + screen.drawImage(Screen.image, 0, 0) + }) + control.eventContext().registerFrameHandler(SCREEN_PRIORITY, () => { + control.enablePerfCounter() + control.__screen.update() + }) + } + } + + export class SceneManager { + scenes: Scene[] + + constructor() { + this.scenes = [] + } + + public pushScene(scene: Scene) { + const currScene = this.currScene() + if (currScene) { + currScene.deactivate() + } + control.pushEventContext() + this.scenes.push(scene) + scene.startup() + scene.activate() + scene.__init() + } + + public popScene() { + const prevScene = this.scenes.pop() + if (prevScene) { + prevScene.deactivate() + prevScene.shutdown() + control.popEventContext() + } + const currScene = this.currScene() + if (currScene) { + currScene.activate() + } + } + + private currScene(): Scene { + if (this.scenes.length) { + return this.scenes[this.scenes.length - 1] + } + return undefined + } + } +} + +// this is needed for compat with most recent version of arcade +namespace game { + export function addScenePushHandler(handler: (oldScene: any) => void) {} + + export function addScenePopHandler(handler: (oldScene: any) => void) {} +} diff --git a/libs/microcode/screen.ts b/libs/microcode/screen.ts new file mode 100644 index 00000000000..ff2ce5dba69 --- /dev/null +++ b/libs/microcode/screen.ts @@ -0,0 +1,367 @@ +namespace microcode { + export class Screen { + private static image_: SImage + + public static WIDTH = screen.width + public static HEIGHT = screen.height + public static HALF_WIDTH = screen.width >> 1 + public static HALF_HEIGHT = screen.height >> 1 + public static LEFT_EDGE = -Screen.HALF_WIDTH + public static RIGHT_EDGE = Screen.HALF_WIDTH + public static TOP_EDGE = -Screen.HALF_HEIGHT + public static BOTTOM_EDGE = Screen.HALF_HEIGHT + public static BOUNDS = new Bounds({ + left: Screen.LEFT_EDGE, + top: Screen.TOP_EDGE, + width: Screen.WIDTH, + height: Screen.HEIGHT, + }) + + private static updateBounds() { + Screen.WIDTH = Screen.image_.width + Screen.HEIGHT = Screen.image_.height + Screen.HALF_WIDTH = Screen.WIDTH >> 1 + Screen.HALF_HEIGHT = Screen.HEIGHT >> 1 + Screen.LEFT_EDGE = -Screen.HALF_WIDTH + Screen.RIGHT_EDGE = Screen.HALF_WIDTH + Screen.TOP_EDGE = -Screen.HALF_HEIGHT + Screen.BOTTOM_EDGE = Screen.HALF_HEIGHT + Screen.BOUNDS = new Bounds({ + left: Screen.LEFT_EDGE, + top: Screen.TOP_EDGE, + width: Screen.WIDTH, + height: Screen.HEIGHT, + }) + } + + public static x(v: number) { + return v + Screen.HALF_WIDTH + } + public static y(v: number) { + return v + Screen.HALF_HEIGHT + } + public static pos(v: Vec2) { + return new Vec2(Screen.x(v.x), Screen.y(v.y)) + } + public static get image(): SImage { + if (!Screen.image_) { + Screen.image_ = screen + Screen.updateBounds() + } + return Screen.image_ + } + public static resetScreenImage() { + Screen.image_ = screen + Screen.updateBounds() + } + + public static setImageSize(width: number, height: number) { + Screen.image_ = image.create(width, height) + Screen.updateBounds() + } + + public static drawTransparentImage(from: SImage, x: number, y: number) { + Screen.image.drawTransparentImage(from, Screen.x(x), Screen.y(y)) + } + + public static drawTransparentImageXfrm( + xfrm: Affine, + from: SImage, + x: number, + y: number + ) { + const w = xfrm.worldPos + Screen.image.drawTransparentImage( + from, + Screen.x(x + w.x), + Screen.y(y + w.y) + ) + } + + public static drawLine( + x0: number, + y0: number, + x1: number, + y1: number, + c: number + ) { + control.assert(c !== 0, "ERROR_NOT_INTEGER") + Screen.image.drawLine( + Screen.x(x0), + Screen.y(y0), + Screen.x(x1), + Screen.y(y1), + c + ) + } + + public static drawLineXfrm( + xfrm: Affine, + x0: number, + y0: number, + x1: number, + y1: number, + c: number + ) { + control.assert(c !== 0, "ERROR_NOT_INTEGER") + const w = xfrm.worldPos + Screen.drawLine(x0 + w.x, y0 + w.y, x1 + w.x, y1 + w.y, c) + } + + public static drawLineShaded( + x0: number, + y0: number, + x1: number, + y1: number, + shader: (x: number, y: number) => number + ) { + let sx0 = Screen.x(x0) + let sy0 = Screen.y(y0) + let sx1 = Screen.x(x1) + let sy1 = Screen.y(y1) + + for (let x = sx0, tx = x0; x <= sx1; x++, tx++) { + for (let y = sy0, ty = y0; y <= sy1; y++, ty++) { + const c = shader(tx, ty) + if (c) { + Screen.image.setPixel(x, y, c) + } + } + } + } + + public static drawRect( + x: number, + y: number, + width: number, + height: number, + c: number + ) { + control.assert(c !== 0, "ERROR_NOT_INTEGER") + Screen.image.drawRect(Screen.x(x), Screen.y(y), width, height, c) + } + + public static drawRectXfrm( + xfrm: Affine, + x: number, + y: number, + width: number, + height: number, + c: number + ) { + control.assert(c !== 0, "ERROR_NOT_INTEGER") + const w = xfrm.worldPos + Screen.drawRect(x + w.x, y + w.y, width, height, c) + } + + public static fillRect( + x: number, + y: number, + width: number, + height: number, + c: number + ) { + control.assert(c !== 0, "ERROR_NOT_INTEGER") + Screen.image.fillRect(Screen.x(x), Screen.y(y), width, height, c) + } + + public static fillRectXfrm( + xfrm: Affine, + x: number, + y: number, + width: number, + height: number, + c: number + ) { + control.assert(c !== 0, "ERROR_NOT_INTEGER") + const w = xfrm.worldPos + Screen.fillRect(x + w.x, y + w.y, width, height, c) + } + + public static fillBoundsXfrm(xfrm: Affine, bounds: Bounds, c: number) { + control.assert(c !== 0, "ERROR_NOT_INTEGER") + Screen.fillRectXfrm( + xfrm, + bounds.left, + bounds.top, + bounds.width, + bounds.height, + c + ) + } + + public static drawBoundsXfrm(xfrm: Affine, bounds: Bounds, c: number) { + control.assert(c !== 0, "ERROR_NOT_INTEGER") + Screen.drawRectXfrm( + xfrm, + bounds.left, + bounds.top, + bounds.width, + bounds.height, + c + ) + } + + // Draws a rounded outline rectangle of the bounds. + public static outlineBoundsXfrm( + xfrm: Affine, + bounds: Bounds, + dist: number, + c: number + ) { + if (!c) return + + const w = xfrm.worldPos + const left = bounds.left + w.x + const top = bounds.top + w.y + const right = bounds.right + w.x + const bottom = bounds.bottom + w.y + + // Left + Screen.drawLine(left - dist, top, left - dist, bottom, c) + // Right + Screen.drawLine(right + dist, top, right + dist, bottom, c) + // Top + Screen.drawLine(left, top - dist, right, top - dist, c) + // Bottom + Screen.drawLine(left, bottom + dist, right, bottom + dist, c) + + // Connect corners + if (dist > 1) { + // Left-Top + Screen.drawLine(left - dist, top, left, top - dist, c) + // Right-Top + Screen.drawLine(right + dist, top, right, top - dist, c) + // Left-Bottom + Screen.drawLine(left - dist, bottom, left, bottom + dist, c) + // Right-Bottom + Screen.drawLine(right + dist, bottom, right, bottom + dist, c) + } + } + + // Draws a rounded outline rectangle of the bounds. + public static outlineBoundsXfrm4( + xfrm: Affine, + bounds: Bounds, + dist: number, + colors: { top: number; left: number; right: number; bottom: number } + ) { + // no borders! + if (!colors.top && !colors.left && !colors.right && !colors.bottom) + return + + const w = xfrm.worldPos + const left = bounds.left + w.x + const top = bounds.top + w.y + const right = bounds.right + w.x + const bottom = bounds.bottom + w.y + + // Left + if (colors.left) + Screen.drawLine( + left - dist, + top, + left - dist, + bottom, + colors.left + ) + // Right + if (colors.right) + Screen.drawLine( + right + dist, + top, + right + dist, + bottom, + colors.right + ) + // Top + if (colors.top) + Screen.drawLine(left, top - dist, right, top - dist, colors.top) + // Bottom + if (colors.bottom) + Screen.drawLine( + left, + bottom + dist, + right, + bottom + dist, + colors.bottom + ) + + // Connect corners + if (dist > 1) { + // Left-Top + if (colors.left) + Screen.drawLine( + left - dist, + top, + left, + top - dist, + colors.left + ) + // Right-Top + if (colors.right) + Screen.drawLine( + right + dist, + top, + right, + top - dist, + colors.right + ) + // Left-Bottom + if (colors.left) + Screen.drawLine( + left - dist, + bottom, + left, + bottom + dist, + colors.left + ) + // Right-Bottom + if (colors.right) + Screen.drawLine( + right + dist, + bottom, + right, + bottom + dist, + colors.right + ) + } + } + + public static setPixel(x: number, y: number, c: number) { + if (c) { + Screen.image.setPixel(Screen.x(x), Screen.y(y), c) + } + } + + public static setPixelXfrm( + xfrm: Affine, + x: number, + y: number, + c: number + ) { + control.assert(c !== 0, "ERROR_NOT_INTEGER") + const w = xfrm.worldPos + Screen.setPixel(x + w.x, y + w.y, c) + } + + public static print( + text: string, + x: number, + y: number, + color?: number, + font?: simage.Font, + offsets?: texteffects.TextEffectState[] + ) { + control.assert(color !== 0, "ERROR_NOT_INTEGER") + Screen.image.print( + text, + Screen.x(x), + Screen.y(y), + color, + font, + offsets + ) + } + } +} diff --git a/libs/microcode/sprite.ts b/libs/microcode/sprite.ts new file mode 100644 index 00000000000..504199299e4 --- /dev/null +++ b/libs/microcode/sprite.ts @@ -0,0 +1,73 @@ +namespace microcode { + export class Sprite implements IComponent, IPlaceable, ISizable { + private xfrm_: Affine + image: SImage + invisible: boolean + + public get xfrm() { + return this.xfrm_ + } + public get width() { + return this.image.width + } + public get height() { + return this.image.height + } + public get hitbox() { + return Bounds.FromSprite(this) + } + + public get bounds() { + let b = new Bounds({ + left: 0, + top: 0, + width: this.width, + height: this.height, + }) + return b.translate( + new Vec2(-(this.width >> 1), -(this.height >> 1)) + ) + } + + constructor(opts: { parent?: IPlaceable; img: SImage }) { + this.xfrm_ = new Affine() + this.xfrm_.parent = opts.parent && opts.parent.xfrm + this.image = opts.img + } + + update() { } + + public setImage(img: SImage) { + this.image = img + } + + public bindXfrm(xfrm: Affine) { + this.xfrm_ = xfrm + } + + public occlusions(bounds: Bounds) { + return Occlusions.FromSprite(this, bounds) + } + + public isOffScreenX(): boolean { + const p = this.xfrm.worldPos + return ( + p.x + (this.width >> 1) < Screen.LEFT_EDGE || + p.x - (this.width >> 1) > Screen.RIGHT_EDGE + ) + } + draw() { + if (this.invisible) { + return + } + Screen.drawTransparentImageXfrm( + this.xfrm, + this.image, + -(this.image.width >> 1), + -(this.image.height >> 1) + ) + } + } + + const _pos: Vec2 = new Vec2(0, 0) +} diff --git a/libs/microcode/tiles.ts b/libs/microcode/tiles.ts new file mode 100644 index 00000000000..87c35924b53 --- /dev/null +++ b/libs/microcode/tiles.ts @@ -0,0 +1,1432 @@ +namespace microcode { + // eventually, we should get rid of these constants and use the Tid enum + + // Once a tid is assigned, it can NEVER BE CHANGED OR REPURPOSED. + // Every tid must be unique in the set of all tids. + export const TID_SENSOR_START_PAGE = "S1" + export const TID_SENSOR_PRESS = "S2" + export const TID_SENSOR_RELEASE = "S2B" + export const TID_SENSOR_ACCELEROMETER = "S3" + export const TID_SENSOR_TIMER = "S4" + export const TID_SENSOR_LIGHT = "S5" + export const TID_SENSOR_TEMP = "S6" + export const TID_SENSOR_RADIO_RECEIVE = "S7" + export const TID_SENSOR_MICROPHONE = "S8" + export const TID_SENSOR_CUP_X_WRITTEN = "S9A" + export const TID_SENSOR_CUP_Y_WRITTEN = "S9B" + export const TID_SENSOR_CUP_Z_WRITTEN = "S9C" + export const TID_SENSOR_MAGNET = "S10" + export const TID_SENSOR_SLIDER = "S11" + export const TID_SENSOR_ROTARY = "S12" + export const TID_SENSOR_CAR_WALL = "S13" + export const TID_SENSOR_LINE = "S14" + export const TID_SENSOR_LED_LIGHT = "S15" + + // filters for TID_SENSOR_PRESS + export const TID_FILTER_PIN_0 = "F0" + export const TID_FILTER_PIN_1 = "F1" + export const TID_FILTER_PIN_2 = "F2" + export const TID_FILTER_BUTTON_A = "F3" + export const TID_FILTER_BUTTON_B = "F4" + export const TID_FILTER_KITA_KEY_1 = "F5" + export const TID_FILTER_KITA_KEY_2 = "F6" + + export const TID_FILTER_LOGO = "F7" + export const TID_FILTER_COIN_1 = "F8" + export const TID_FILTER_COIN_2 = "F9" + export const TID_FILTER_COIN_3 = "F10" + export const TID_FILTER_COIN_4 = "F11" + export const TID_FILTER_COIN_5 = "F12" + export const TID_FILTER_TIMESPAN_SHORT = "F13" + export const TID_FILTER_TIMESPAN_LONG = "F14" + export const TID_FILTER_LOUD = "F15" + export const TID_FILTER_QUIET = "F16" + export const TID_FILTER_ACCEL = "F17" + export const TID_FILTER_ACCEL_SHAKE = "F17_shake" + export const TID_FILTER_ACCEL_TILT_UP = "F17_tilt_up" + export const TID_FILTER_ACCEL_TILT_DOWN = "F17_tilt_down" + export const TID_FILTER_ACCEL_TILT_LEFT = "F17_tilt_left" + export const TID_FILTER_ACCEL_TILT_RIGHT = "F17_tilt_right" + export const TID_FILTER_ACCEL_FACE_UP = "F17_face_up" + export const TID_FILTER_ACCEL_FACE_DOWN = "F17_face_down" + export const TID_FILTER_TIMESPAN_RANDOM = "F18" + export const TID_FILTER_TIMESPAN_VERY_LONG = "F19" + export const TID_FILTER_CUP_X_READ = "F20A" + export const TID_FILTER_CUP_Y_READ = "F20B" + export const TID_FILTER_CUP_Z_READ = "F20C" + export const TID_FILTER_ROTARY_LEFT = "F21L" + export const TID_FILTER_ROTARY_RIGHT = "F21R" + export const TID_FILTER_TEMP_WARMER = "F22U" + export const TID_FILTER_TEMP_COLDER = "F22D" + export const TID_FILTER_LINE_LEFT = "F23L" + export const TID_FILTER_LINE_RIGHT = "F23R" + export const TID_FILTER_LINE_BOTH = "F23B" + export const TID_FILTER_LINE_NEITHER = "F23N" + export const TID_FILTER_LINE_NEITHER_LEFT = "F23NL" + export const TID_FILTER_LINE_NEITHER_RIGHT = "F23NR" + + export const TID_ACTUATOR_SWITCH_PAGE = "A1" + export const TID_ACTUATOR_SPEAKER = "A2" + export const TID_ACTUATOR_MICROPHONE = "A3" // dead but don't delete + export const TID_ACTUATOR_MUSIC = "A4" + export const TID_ACTUATOR_PAINT = "A5" + export const TID_ACTUATOR_RADIO_SEND = "A6" + export const TID_ACTUATOR_RADIO_SET_GROUP = "A6A" + export const TID_ACTUATOR_RGB_LED = "A8" + export const TID_ACTUATOR_CUP_X_ASSIGN = "A9A" + export const TID_ACTUATOR_CUP_Y_ASSIGN = "A9B" + export const TID_ACTUATOR_CUP_Z_ASSIGN = "A9C" + export const TID_ACTUATOR_SHOW_NUMBER = "A10" + + export const TID_MODIFIER_PAGE_1 = "M1" + export const TID_MODIFIER_PAGE_2 = "M2" + export const TID_MODIFIER_PAGE_3 = "M3" + export const TID_MODIFIER_PAGE_4 = "M4" + export const TID_MODIFIER_PAGE_5 = "M5" + + export const TID_MODIFIER_COIN_1 = "M6" + export const TID_MODIFIER_COIN_2 = "M7" + export const TID_MODIFIER_COIN_3 = "M8" + export const TID_MODIFIER_COIN_4 = "M9" + export const TID_MODIFIER_COIN_5 = "M10" + + export const TID_MODIFIER_ICON_EDITOR = "M15" + export const TID_MODIFIER_COLOR_RED = "M16" + export const TID_MODIFIER_COLOR_DARKPURPLE = "M17" + + export const TID_MODIFIER_EMOJI_GIGGLE = "M19giggle" + export const TID_MODIFIER_EMOJI_HAPPY = "M19happy" + export const TID_MODIFIER_EMOJI_HELLO = "M19hello" + export const TID_MODIFIER_EMOJI_MYSTERIOUS = "M19mysterious" + export const TID_MODIFIER_EMOJI_SAD = "M19sad" + export const TID_MODIFIER_EMOJI_SLIDE = "M19slide" + export const TID_MODIFIER_EMOJI_SOARING = "M19soaring" + export const TID_MODIFIER_EMOJI_SPRING = "M19spring" + export const TID_MODIFIER_EMOJI_TWINKLE = "M19twinkle" + export const TID_MODIFIER_EMOJI_YAWN = "M19yawn" + + export const TID_MODIFIER_CUP_X_READ = "M20A" + export const TID_MODIFIER_CUP_Y_READ = "M20B" + export const TID_MODIFIER_CUP_Z_READ = "M20C" + export const TID_MODIFIER_RADIO_VALUE = "M21" + export const TID_MODIFIER_RANDOM_TOSS = "M22" + export const TID_MODIFIER_LOOP = "M23" + export const TID_MODIFIER_MELODY_EDITOR = "M24" + export const TID_MODIFIER_TEMP_READ = "M25" + + export const TID_MODIFIER_RGB_LED_COLOR_X = "A20_" + export const TID_MODIFIER_RGB_LED_COLOR_1 = "A20_1" + export const TID_MODIFIER_RGB_LED_COLOR_2 = "A20_2" + export const TID_MODIFIER_RGB_LED_COLOR_3 = "A20_3" + export const TID_MODIFIER_RGB_LED_COLOR_4 = "A20_4" + export const TID_MODIFIER_RGB_LED_COLOR_5 = "A20_5" + export const TID_MODIFIER_RGB_LED_COLOR_6 = "A20_6" + export const TID_MODIFIER_RGB_LED_COLOR_RAINBOW = "A20_rainbow" + export const TID_MODIFIER_RGB_LED_COLOR_SPARKLE = "A20_sparkle" + + export const TID_ACTUATOR_SERVO_SET_ANGLE = "A21_" + + export const TID_ACTUATOR_CAR = "CAR" + export const TID_MODIFIER_CAR_FORWARD = "CAR1" + export const TID_MODIFIER_CAR_REVERSE = "CAR2" + export const TID_MODIFIER_CAR_TURN_LEFT = "CAR3" + export const TID_MODIFIER_CAR_TURN_RIGHT = "CAR4" + export const TID_MODIFIER_CAR_STOP = "CAR5" + export const TID_MODIFIER_CAR_FORWARD_FAST = "CAR6" + export const TID_MODIFIER_CAR_SPIN_LEFT = "CAR7" + export const TID_MODIFIER_CAR_SPIN_RIGHT = "CAR8" + export const TID_MODIFIER_CAR_LED_COLOR_1 = "CAR9" + export const TID_MODIFIER_CAR_LED_COLOR_2 = "CAR10" + export const TID_MODIFIER_CAR_LED_COLOR_3 = "CAR11" + export const TID_MODIFIER_CAR_LED_COLOR_4 = "CAR12" + export const TID_MODIFIER_CAR_ARM_OPEN = "CAR13" + export const TID_MODIFIER_CAR_ARM_CLOSE = "CAR14" + + // DO NOT CHANGE THESE NUMBERS + export enum Tid { + // we need markers to indicate the end of a program, page + END_OF_PROG = 0, + END_OF_PAGE, + + SENSOR_START = 10, + TID_SENSOR_START_PAGE = 10, + TID_SENSOR_PRESS = 11, + TID_SENSOR_RELEASE = 12, + TID_SENSOR_ACCELEROMETER = 13, + TID_SENSOR_TIMER = 14, + TID_SENSOR_LIGHT = 15, // this is jacdac only + TID_SENSOR_TEMP = 16, + TID_SENSOR_RADIO_RECEIVE = 17, + TID_SENSOR_MICROPHONE = 18, + TID_SENSOR_CUP_X_WRITTEN = 19, + TID_SENSOR_CUP_Y_WRITTEN = 20, + TID_SENSOR_CUP_Z_WRITTEN = 21, + TID_SENSOR_MAGNET = 22, + TID_SENSOR_SLIDER = 23, + TID_SENSOR_ROTARY = 24, + TID_SENSOR_CAR_WALL = 25, + TID_SENSOR_LINE = 26, + TID_SENSOR_LED_LIGHT = 27, // this built-in light sensor on microbit + SENSOR_END = 27, + + ACTUATOR_START = 40, + TID_ACTUATOR_SWITCH_PAGE = 40, + TID_ACTUATOR_SPEAKER = 41, + TID_ACTUATOR_MICROPHONE = 42, // dead, but don't delete + TID_ACTUATOR_MUSIC = 43, + TID_ACTUATOR_PAINT = 44, + TID_ACTUATOR_RADIO_SEND = 45, + TID_ACTUATOR_RADIO_SET_GROUP = 46, + TID_ACTUATOR_RGB_LED = 47, + TID_ACTUATOR_CUP_X_ASSIGN = 48, + TID_ACTUATOR_CUP_Y_ASSIGN = 49, + TID_ACTUATOR_CUP_Z_ASSIGN = 50, + TID_ACTUATOR_SHOW_NUMBER = 51, + TID_ACTUATOR_CAR = 52, + TID_ACTUATOR_SERVO_SET_ANGLE = 53, + ACTUATOR_END = 53, + + FILTER_START = 70, + PRESS_RELEASE_START = 70, + TID_FILTER_PIN_0 = 70, + TID_FILTER_PIN_1 = 71, + TID_FILTER_PIN_2 = 72, + TID_FILTER_BUTTON_A = 73, + TID_FILTER_BUTTON_B = 74, + TID_FILTER_KITA_KEY_1 = 75, + TID_FILTER_KITA_KEY_2 = 76, + TID_FILTER_LOGO = 77, + PRESS_RELEASE_END = 77, + // + TID_FILTER_COIN_1 = 78, + TID_FILTER_COIN_2 = 79, + TID_FILTER_COIN_3 = 80, + TID_FILTER_COIN_4 = 81, + TID_FILTER_COIN_5 = 82, + // + TID_FILTER_TIMESPAN_SHORT = 83, + TID_FILTER_TIMESPAN_LONG = 84, + TID_FILTER_TIMESPAN_RANDOM = 85, + TID_FILTER_TIMESPAN_VERY_LONG = 86, + // + TID_FILTER_LOUD = 87, + TID_FILTER_QUIET = 88, + // + TID_FILTER_ACCEL = 89, // dead (AFAIK) + ACCELEROMETER_START = 90, + TID_FILTER_ACCEL_SHAKE = 90, + TID_FILTER_ACCEL_TILT_UP = 91, + TID_FILTER_ACCEL_TILT_DOWN = 92, + TID_FILTER_ACCEL_TILT_LEFT = 93, + TID_FILTER_ACCEL_TILT_RIGHT = 94, + ACCELEROMETER_END = 94, + // + TID_FILTER_CUP_X_READ = 95, + TID_FILTER_CUP_Y_READ = 96, + TID_FILTER_CUP_Z_READ = 97, + // + TID_FILTER_ROTARY_LEFT = 98, + TID_FILTER_ROTARY_RIGHT = 99, + // + TID_FILTER_TEMP_WARMER = 100, + TID_FILTER_TEMP_COLDER = 101, + // + LINE_START = 102, + TID_FILTER_LINE_LEFT = 102, + TID_FILTER_LINE_RIGHT = 103, + TID_FILTER_LINE_BOTH = 104, + TID_FILTER_LINE_NEITHER = 105, + TID_FILTER_LINE_NEITHER_LEFT = 106, + TID_FILTER_LINE_NEITHER_RIGHT = 107, + LINE_END = 107, + + ACCELEROMETER_START2 = 108, + TID_FILTER_ACCEL_FACE_UP = 108, + TID_FILTER_ACCEL_FACE_DOWN = 109, + ACCELEROMETER_END2 = 109, + + FILTER_END = 109, + + MODIFIER_START = 150, + // + TID_MODIFIER_PAGE_1 = 150, + TID_MODIFIER_PAGE_2 = 151, + TID_MODIFIER_PAGE_3 = 152, + TID_MODIFIER_PAGE_4 = 153, + TID_MODIFIER_PAGE_5 = 154, + // + TID_MODIFIER_COIN_1 = 155, + TID_MODIFIER_COIN_2 = 156, + TID_MODIFIER_COIN_3 = 157, + TID_MODIFIER_COIN_4 = 158, + TID_MODIFIER_COIN_5 = 159, + // + TID_MODIFIER_ICON_EDITOR = 160, + TID_MODIFIER_COLOR_RED = 161, + TID_MODIFIER_COLOR_DARKPURPLE = 162, + // + EMOJI_BEGIN = 163, + TID_MODIFIER_EMOJI_GIGGLE = 163, + TID_MODIFIER_EMOJI_HAPPY = 164, + TID_MODIFIER_EMOJI_HELLO = 165, + TID_MODIFIER_EMOJI_MYSTERIOUS = 166, + TID_MODIFIER_EMOJI_SAD = 167, + TID_MODIFIER_EMOJI_SLIDE = 168, + TID_MODIFIER_EMOJI_SOARING = 169, + TID_MODIFIER_EMOJI_SPRING = 170, + TID_MODIFIER_EMOJI_TWINKLE = 171, + TID_MODIFIER_EMOJI_YAWN = 172, + EMOJI_END = 172, + // + TID_MODIFIER_CUP_X_READ = 173, + TID_MODIFIER_CUP_Y_READ = 174, + TID_MODIFIER_CUP_Z_READ = 175, + TID_MODIFIER_RADIO_VALUE = 176, + TID_MODIFIER_RANDOM_TOSS = 177, + TID_MODIFIER_LOOP = 178, + TID_MODIFIER_MELODY_EDITOR = 179, + TID_MODIFIER_TEMP_READ = 180, + // + TID_MODIFIER_RGB_LED_COLOR_X = 181, + TID_MODIFIER_RGB_LED_COLOR_1 = 182, + TID_MODIFIER_RGB_LED_COLOR_2 = 183, + TID_MODIFIER_RGB_LED_COLOR_3 = 184, + TID_MODIFIER_RGB_LED_COLOR_4 = 185, + TID_MODIFIER_RGB_LED_COLOR_5 = 186, + TID_MODIFIER_RGB_LED_COLOR_6 = 187, + TID_MODIFIER_RGB_LED_COLOR_RAINBOW = 188, + TID_MODIFIER_RGB_LED_COLOR_SPARKLE = 189, + // + CAR_MODIFIER_BEGIN = 190, + TID_MODIFIER_CAR_FORWARD = 190, + TID_MODIFIER_CAR_REVERSE = 191, + TID_MODIFIER_CAR_TURN_LEFT = 192, + TID_MODIFIER_CAR_TURN_RIGHT = 193, + TID_MODIFIER_CAR_STOP = 194, + TID_MODIFIER_CAR_FORWARD_FAST = 195, + TID_MODIFIER_CAR_SPIN_LEFT = 196, + TID_MODIFIER_CAR_SPIN_RIGHT = 197, + TID_MODIFIER_CAR_LED_COLOR_1 = 198, + TID_MODIFIER_CAR_LED_COLOR_2 = 199, + TID_MODIFIER_CAR_LED_COLOR_3 = 200, + TID_MODIFIER_CAR_LED_COLOR_4 = 201, + TID_MODIFIER_CAR_ARM_OPEN = 202, + TID_MODIFIER_CAR_ARM_CLOSE = 203, + CAR_MODIFIER_END = 203, + MODIFER_END = 203, + } + + type RangeMap = { [id: string]: [Tid, Tid] } + + export const ranges: RangeMap = { + sensors: [Tid.SENSOR_START, Tid.SENSOR_END], + filters: [Tid.FILTER_START, Tid.FILTER_END], + actuators: [Tid.ACTUATOR_START, Tid.ACTUATOR_END], + modifiers: [Tid.MODIFIER_START, Tid.MODIFER_END], + } + + export function tidToString(e: Tid) { + switch (e) { + case Tid.TID_SENSOR_START_PAGE: + return TID_SENSOR_START_PAGE + case Tid.TID_SENSOR_PRESS: + return TID_SENSOR_PRESS + case Tid.TID_SENSOR_RELEASE: + return TID_SENSOR_RELEASE + case Tid.TID_SENSOR_ACCELEROMETER: + return TID_SENSOR_ACCELEROMETER + case Tid.TID_SENSOR_TIMER: + return TID_SENSOR_TIMER + case Tid.TID_SENSOR_LIGHT: + return TID_SENSOR_LIGHT + case Tid.TID_SENSOR_LED_LIGHT: + return TID_SENSOR_LED_LIGHT + case Tid.TID_SENSOR_TEMP: + return TID_SENSOR_TEMP + case Tid.TID_SENSOR_RADIO_RECEIVE: + return TID_SENSOR_RADIO_RECEIVE + case Tid.TID_SENSOR_MICROPHONE: + return TID_SENSOR_MICROPHONE + case Tid.TID_SENSOR_CUP_X_WRITTEN: + return TID_SENSOR_CUP_X_WRITTEN + case Tid.TID_SENSOR_CUP_Y_WRITTEN: + return TID_SENSOR_CUP_Y_WRITTEN + case Tid.TID_SENSOR_CUP_Z_WRITTEN: + return TID_SENSOR_CUP_Z_WRITTEN + case Tid.TID_SENSOR_MAGNET: + return TID_SENSOR_MAGNET + case Tid.TID_SENSOR_SLIDER: + return TID_SENSOR_SLIDER + case Tid.TID_SENSOR_ROTARY: + return TID_SENSOR_ROTARY + case Tid.TID_SENSOR_CAR_WALL: + return TID_SENSOR_CAR_WALL + case Tid.TID_SENSOR_LINE: + return TID_SENSOR_LINE + + case Tid.TID_FILTER_PIN_0: + return TID_FILTER_PIN_0 + case Tid.TID_FILTER_PIN_1: + return TID_FILTER_PIN_1 + case Tid.TID_FILTER_PIN_2: + return TID_FILTER_PIN_2 + case Tid.TID_FILTER_BUTTON_A: + return TID_FILTER_BUTTON_A + case Tid.TID_FILTER_BUTTON_B: + return TID_FILTER_BUTTON_B + case Tid.TID_FILTER_KITA_KEY_1: + return TID_FILTER_KITA_KEY_1 + case Tid.TID_FILTER_KITA_KEY_2: + return TID_FILTER_KITA_KEY_2 + case Tid.TID_FILTER_LOGO: + return TID_FILTER_LOGO + case Tid.TID_FILTER_COIN_1: + return TID_FILTER_COIN_1 + case Tid.TID_FILTER_COIN_2: + return TID_FILTER_COIN_2 + case Tid.TID_FILTER_COIN_3: + return TID_FILTER_COIN_3 + case Tid.TID_FILTER_COIN_4: + return TID_FILTER_COIN_4 + case Tid.TID_FILTER_COIN_5: + return TID_FILTER_COIN_5 + case Tid.TID_FILTER_TIMESPAN_SHORT: + return TID_FILTER_TIMESPAN_SHORT + case Tid.TID_FILTER_TIMESPAN_LONG: + return TID_FILTER_TIMESPAN_LONG + case Tid.TID_FILTER_LOUD: + return TID_FILTER_LOUD + case Tid.TID_FILTER_QUIET: + return TID_FILTER_QUIET + case Tid.TID_FILTER_ACCEL: + return TID_FILTER_ACCEL + case Tid.TID_FILTER_ACCEL_SHAKE: + return TID_FILTER_ACCEL_SHAKE + case Tid.TID_FILTER_ACCEL_TILT_UP: + return TID_FILTER_ACCEL_TILT_UP + case Tid.TID_FILTER_ACCEL_TILT_DOWN: + return TID_FILTER_ACCEL_TILT_DOWN + case Tid.TID_FILTER_ACCEL_TILT_LEFT: + return TID_FILTER_ACCEL_TILT_LEFT + case Tid.TID_FILTER_ACCEL_FACE_DOWN: + return TID_FILTER_ACCEL_FACE_DOWN + case Tid.TID_FILTER_ACCEL_FACE_UP: + return TID_FILTER_ACCEL_FACE_UP + case Tid.TID_FILTER_ACCEL_TILT_RIGHT: + return TID_FILTER_ACCEL_TILT_RIGHT + case Tid.TID_FILTER_TIMESPAN_RANDOM: + return TID_FILTER_TIMESPAN_RANDOM + case Tid.TID_FILTER_TIMESPAN_VERY_LONG: + return TID_FILTER_TIMESPAN_VERY_LONG + case Tid.TID_FILTER_CUP_X_READ: + return TID_FILTER_CUP_X_READ + case Tid.TID_FILTER_CUP_Y_READ: + return TID_FILTER_CUP_Y_READ + case Tid.TID_FILTER_CUP_Z_READ: + return TID_FILTER_CUP_Z_READ + case Tid.TID_FILTER_ROTARY_LEFT: + return TID_FILTER_ROTARY_LEFT + case Tid.TID_FILTER_ROTARY_RIGHT: + return TID_FILTER_ROTARY_RIGHT + case Tid.TID_FILTER_TEMP_WARMER: + return TID_FILTER_TEMP_WARMER + case Tid.TID_FILTER_TEMP_COLDER: + return TID_FILTER_TEMP_COLDER + case Tid.TID_FILTER_LINE_LEFT: + return TID_FILTER_LINE_LEFT + case Tid.TID_FILTER_LINE_RIGHT: + return TID_FILTER_LINE_RIGHT + case Tid.TID_FILTER_LINE_BOTH: + return TID_FILTER_LINE_BOTH + case Tid.TID_FILTER_LINE_NEITHER: + return TID_FILTER_LINE_NEITHER + case Tid.TID_FILTER_LINE_NEITHER_LEFT: + return TID_FILTER_LINE_NEITHER_LEFT + case Tid.TID_FILTER_LINE_NEITHER_RIGHT: + return TID_FILTER_LINE_NEITHER_RIGHT + + case Tid.TID_ACTUATOR_SWITCH_PAGE: + return TID_ACTUATOR_SWITCH_PAGE + case Tid.TID_ACTUATOR_SPEAKER: + return TID_ACTUATOR_SPEAKER + case Tid.TID_ACTUATOR_MUSIC: + return TID_ACTUATOR_MUSIC + case Tid.TID_ACTUATOR_PAINT: + return TID_ACTUATOR_PAINT + case Tid.TID_ACTUATOR_RADIO_SEND: + return TID_ACTUATOR_RADIO_SEND + case Tid.TID_ACTUATOR_RADIO_SET_GROUP: + return TID_ACTUATOR_RADIO_SET_GROUP + case Tid.TID_ACTUATOR_RGB_LED: + return TID_ACTUATOR_RGB_LED + case Tid.TID_ACTUATOR_CUP_X_ASSIGN: + return TID_ACTUATOR_CUP_X_ASSIGN + case Tid.TID_ACTUATOR_CUP_Y_ASSIGN: + return TID_ACTUATOR_CUP_Y_ASSIGN + case Tid.TID_ACTUATOR_CUP_Z_ASSIGN: + return TID_ACTUATOR_CUP_Z_ASSIGN + case Tid.TID_ACTUATOR_SHOW_NUMBER: + return TID_ACTUATOR_SHOW_NUMBER + + case Tid.TID_MODIFIER_PAGE_1: + return TID_MODIFIER_PAGE_1 + case Tid.TID_MODIFIER_PAGE_2: + return TID_MODIFIER_PAGE_2 + case Tid.TID_MODIFIER_PAGE_3: + return TID_MODIFIER_PAGE_3 + case Tid.TID_MODIFIER_PAGE_4: + return TID_MODIFIER_PAGE_4 + case Tid.TID_MODIFIER_PAGE_5: + return TID_MODIFIER_PAGE_5 + + case Tid.TID_MODIFIER_COIN_1: + return TID_MODIFIER_COIN_1 + case Tid.TID_MODIFIER_COIN_2: + return TID_MODIFIER_COIN_2 + case Tid.TID_MODIFIER_COIN_3: + return TID_MODIFIER_COIN_3 + case Tid.TID_MODIFIER_COIN_4: + return TID_MODIFIER_COIN_4 + case Tid.TID_MODIFIER_COIN_5: + return TID_MODIFIER_COIN_5 + + case Tid.TID_MODIFIER_ICON_EDITOR: + return TID_MODIFIER_ICON_EDITOR + + case Tid.TID_MODIFIER_COLOR_RED: + return TID_MODIFIER_COLOR_RED + case Tid.TID_MODIFIER_COLOR_DARKPURPLE: + return TID_MODIFIER_COLOR_DARKPURPLE + + case Tid.TID_MODIFIER_EMOJI_GIGGLE: + return TID_MODIFIER_EMOJI_GIGGLE + case Tid.TID_MODIFIER_EMOJI_HAPPY: + return TID_MODIFIER_EMOJI_HAPPY + case Tid.TID_MODIFIER_EMOJI_HELLO: + return TID_MODIFIER_EMOJI_HELLO + case Tid.TID_MODIFIER_EMOJI_MYSTERIOUS: + return TID_MODIFIER_EMOJI_MYSTERIOUS + case Tid.TID_MODIFIER_EMOJI_SAD: + return TID_MODIFIER_EMOJI_SAD + case Tid.TID_MODIFIER_EMOJI_SLIDE: + return TID_MODIFIER_EMOJI_SLIDE + case Tid.TID_MODIFIER_EMOJI_SOARING: + return TID_MODIFIER_EMOJI_SOARING + case Tid.TID_MODIFIER_EMOJI_SPRING: + return TID_MODIFIER_EMOJI_SPRING + case Tid.TID_MODIFIER_EMOJI_TWINKLE: + return TID_MODIFIER_EMOJI_TWINKLE + case Tid.TID_MODIFIER_EMOJI_YAWN: + return TID_MODIFIER_EMOJI_YAWN + + case Tid.TID_MODIFIER_CUP_X_READ: + return TID_MODIFIER_CUP_X_READ + case Tid.TID_MODIFIER_CUP_Y_READ: + return TID_MODIFIER_CUP_Y_READ + case Tid.TID_MODIFIER_CUP_Z_READ: + return TID_MODIFIER_CUP_Z_READ + + case Tid.TID_MODIFIER_RADIO_VALUE: + return TID_MODIFIER_RADIO_VALUE + case Tid.TID_MODIFIER_RANDOM_TOSS: + return TID_MODIFIER_RANDOM_TOSS + + case Tid.TID_MODIFIER_LOOP: + return TID_MODIFIER_LOOP + + case Tid.TID_MODIFIER_MELODY_EDITOR: + return TID_MODIFIER_MELODY_EDITOR + + case Tid.TID_MODIFIER_TEMP_READ: + return TID_MODIFIER_TEMP_READ + + case Tid.TID_MODIFIER_RGB_LED_COLOR_X: + return TID_MODIFIER_RGB_LED_COLOR_X + case Tid.TID_MODIFIER_RGB_LED_COLOR_1: + return TID_MODIFIER_RGB_LED_COLOR_1 + case Tid.TID_MODIFIER_RGB_LED_COLOR_2: + return TID_MODIFIER_RGB_LED_COLOR_2 + case Tid.TID_MODIFIER_RGB_LED_COLOR_3: + return TID_MODIFIER_RGB_LED_COLOR_3 + case Tid.TID_MODIFIER_RGB_LED_COLOR_4: + return TID_MODIFIER_RGB_LED_COLOR_4 + case Tid.TID_MODIFIER_RGB_LED_COLOR_5: + return TID_MODIFIER_RGB_LED_COLOR_5 + case Tid.TID_MODIFIER_RGB_LED_COLOR_6: + return TID_MODIFIER_RGB_LED_COLOR_6 + + case Tid.TID_MODIFIER_RGB_LED_COLOR_RAINBOW: + return TID_MODIFIER_RGB_LED_COLOR_RAINBOW + case Tid.TID_MODIFIER_RGB_LED_COLOR_SPARKLE: + return TID_MODIFIER_RGB_LED_COLOR_SPARKLE + + case Tid.TID_ACTUATOR_SERVO_SET_ANGLE: + return TID_ACTUATOR_SERVO_SET_ANGLE + + case Tid.TID_ACTUATOR_CAR: + return TID_ACTUATOR_CAR + case Tid.TID_MODIFIER_CAR_FORWARD: + return TID_MODIFIER_CAR_FORWARD + case Tid.TID_MODIFIER_CAR_REVERSE: + return TID_MODIFIER_CAR_REVERSE + case Tid.TID_MODIFIER_CAR_TURN_LEFT: + return TID_MODIFIER_CAR_TURN_LEFT + case Tid.TID_MODIFIER_CAR_TURN_RIGHT: + return TID_MODIFIER_CAR_TURN_RIGHT + case Tid.TID_MODIFIER_CAR_STOP: + return TID_MODIFIER_CAR_STOP + case Tid.TID_MODIFIER_CAR_FORWARD_FAST: + return TID_MODIFIER_CAR_FORWARD_FAST + case Tid.TID_MODIFIER_CAR_SPIN_LEFT: + return TID_MODIFIER_CAR_SPIN_LEFT + case Tid.TID_MODIFIER_CAR_SPIN_RIGHT: + return TID_MODIFIER_CAR_SPIN_RIGHT + case Tid.TID_MODIFIER_CAR_LED_COLOR_1: + return TID_MODIFIER_CAR_LED_COLOR_1 + case Tid.TID_MODIFIER_CAR_LED_COLOR_2: + return TID_MODIFIER_CAR_LED_COLOR_2 + case Tid.TID_MODIFIER_CAR_LED_COLOR_3: + return TID_MODIFIER_CAR_LED_COLOR_3 + case Tid.TID_MODIFIER_CAR_LED_COLOR_4: + return TID_MODIFIER_CAR_LED_COLOR_4 + case Tid.TID_MODIFIER_CAR_ARM_OPEN: + return TID_MODIFIER_CAR_ARM_OPEN + case Tid.TID_MODIFIER_CAR_ARM_CLOSE: + return TID_MODIFIER_CAR_ARM_CLOSE + default: + assert(false, "unknown tid: " + e) + return undefined + } + } + + export function isSensor(tid: Tid) { + return tid >= Tid.SENSOR_START && tid <= Tid.SENSOR_END + } + + export function isFilter(tid: Tid) { + return tid >= Tid.FILTER_START && tid <= Tid.FILTER_END + } + + export function isActuator(tid: Tid) { + return tid >= Tid.ACTUATOR_START && tid <= Tid.ACTUATOR_END + } + + export function isModifier(tid: Tid) { + return tid >= Tid.MODIFIER_START && tid <= Tid.MODIFER_END + } + + function isPressReleaseEvent(tidEnum: Tid) { + return ( + Tid.PRESS_RELEASE_START <= tidEnum && + tidEnum <= Tid.PRESS_RELEASE_END + ) + } + + function isAccelerometerEvent(tidEnum: Tid) { + return ( + Tid.ACCELEROMETER_START <= tidEnum && + tidEnum <= Tid.ACCELEROMETER_END || + Tid.ACCELEROMETER_START2 <= tidEnum && + tidEnum <= Tid.ACCELEROMETER_END2 + ) + } + + function isLineEvent(tidEnum: Tid) { + return Tid.LINE_START <= tidEnum && tidEnum <= Tid.LINE_END + } + + function isFilterConstant(tidEnum: Tid) { + return ( + Tid.TID_FILTER_COIN_1 <= tidEnum && tidEnum <= Tid.TID_FILTER_COIN_5 + ) + } + + function isFilterVariable(tidEnum: Tid) { + return ( + Tid.TID_FILTER_CUP_X_READ <= tidEnum && + tidEnum <= Tid.TID_FILTER_CUP_Z_READ + ) + } + + function isModifierConstant(tidEnum: Tid) { + return ( + Tid.TID_MODIFIER_COIN_1 <= tidEnum && + tidEnum <= Tid.TID_MODIFIER_COIN_5 + ) + } + + function isModifierVariable(tidEnum: Tid) { + return ( + Tid.TID_MODIFIER_CUP_X_READ <= tidEnum && + tidEnum <= Tid.TID_MODIFIER_CUP_Z_READ + ) + } + + function isTimespan(tidEnum: Tid) { + return ( + Tid.TID_FILTER_TIMESPAN_SHORT <= tidEnum && + tidEnum <= Tid.TID_FILTER_TIMESPAN_VERY_LONG + ) + } + + function isEmoji(tidEnum: Tid) { + return Tid.EMOJI_BEGIN <= tidEnum && tidEnum <= Tid.EMOJI_END + } + + function isPage(tidEnum: Tid) { + return ( + Tid.TID_MODIFIER_PAGE_1 <= tidEnum && + tidEnum <= Tid.TID_MODIFIER_PAGE_5 + ) + } + + function isLedColor(tidEnum: Tid) { + return ( + Tid.TID_MODIFIER_RGB_LED_COLOR_1 <= tidEnum && + tidEnum <= Tid.TID_MODIFIER_RGB_LED_COLOR_6 + ) + } + function isLedModifier(tidEnum: Tid) { + return ( + isLedColor(tidEnum) || + tidEnum == Tid.TID_MODIFIER_RGB_LED_COLOR_RAINBOW || + tidEnum == Tid.TID_MODIFIER_RGB_LED_COLOR_SPARKLE + ) + } + + function isCarModifier(tidEnum: Tid) { + return ( + Tid.CAR_MODIFIER_BEGIN <= tidEnum && tidEnum <= Tid.CAR_MODIFIER_END + ) + } + + export function isTerminal(tile: Tile) { + const tid = getTid(tile) + // the following sensors and actuators are terminal + if ( + tid == Tid.TID_SENSOR_CAR_WALL || + tid == Tid.TID_SENSOR_SLIDER || + tid == Tid.TID_ACTUATOR_SWITCH_PAGE || + tid == Tid.TID_SENSOR_LIGHT || + tid == Tid.TID_SENSOR_LED_LIGHT || + tid == Tid.TID_SENSOR_MICROPHONE || + tid == Tid.TID_SENSOR_MAGNET + ) + return true + // everything else except some filters is not terminal + if (!isFilter(tid)) return false + // the following filters are not terminal + if (isFilterConstant(tid) || isTimespan(tid) || isFilterVariable(tid)) + return false + // all other filters are terminal + return true + } + + export function isVisible(tile: Tile) { + const tid = getTid(tile) + // these tids are dead + if (tid == Tid.TID_ACTUATOR_MICROPHONE || tid == Tid.TID_FILTER_ACCEL) + return false + const ext = jdExternalClass(tile) + if (ext && !jacs.debugOut) { + const count = 0 // jdc.numServiceInstances(ext) + // special case for buttons, which already exist on micro:bit (6 of them) + // we also have light sensor on board micro:bit (1 of them), as well as in Kit A + return ext == jacs.ServiceClass.Button + ? count > 6 + : ext == jacs.ServiceClass.LightLevel + ? count > 1 + : count > 0 + } + return true + } + + export function defaultModifier(tid: Tid): Tile { + switch (tid) { + case Tid.TID_ACTUATOR_SPEAKER: + return Tid.TID_MODIFIER_EMOJI_GIGGLE + case Tid.TID_ACTUATOR_CAR: + return Tid.TID_MODIFIER_CAR_STOP + case Tid.TID_ACTUATOR_RGB_LED: + return Tid.TID_MODIFIER_RGB_LED_COLOR_RAINBOW + case Tid.TID_ACTUATOR_PAINT: { + return getEditor(Tid.TID_MODIFIER_ICON_EDITOR) + } + case Tid.TID_ACTUATOR_MUSIC: { + return getEditor(Tid.TID_MODIFIER_MELODY_EDITOR) + } + default: + return undefined + } + } + + export function buttonStyle(tile: Tile): ButtonStyle { + return getFieldEditor(tile) + ? ButtonStyles.Transparent + : ButtonStyles.FlatWhite + } + + export function priority(tile: Tile): number { + const tid = getTid(tile) + if (isFilter(tid)) { + if (isFilterConstant(tid) || isPressReleaseEvent(tid)) + return jdParam(tid) + if (isLineEvent(tid)) { + if (tid == Tid.TID_FILTER_LINE_BOTH) return 101 + else return tid + } + switch (tid) { + case Tid.TID_FILTER_TIMESPAN_SHORT: + return 10 + case Tid.TID_FILTER_TIMESPAN_LONG: + return 20 + case Tid.TID_FILTER_TIMESPAN_VERY_LONG: + return 30 + case Tid.TID_FILTER_TIMESPAN_RANDOM: + return 40 + } + return tid + } else if (isModifier(tid)) { + if (tid == Tid.TID_MODIFIER_LOOP) + // loop always at end + return 1000 + return tid + } + switch (tid) { + // sensors + case Tid.TID_SENSOR_PRESS: + return 9 + case Tid.TID_SENSOR_RELEASE: + return 10 + case Tid.TID_SENSOR_ACCELEROMETER: + return 20 + case Tid.TID_SENSOR_MICROPHONE: + return 30 + case Tid.TID_SENSOR_TEMP: + return 40 + case Tid.TID_SENSOR_LED_LIGHT: + return 50 + case Tid.TID_SENSOR_RADIO_RECEIVE: + return 100 + case Tid.TID_SENSOR_TIMER: + return 110 + case Tid.TID_SENSOR_START_PAGE: + return 108 + case Tid.TID_SENSOR_CUP_X_WRITTEN: + return 200 + case Tid.TID_SENSOR_CUP_Y_WRITTEN: + return 201 + case Tid.TID_SENSOR_CUP_Z_WRITTEN: + return 202 + // Robot car + case Tid.TID_SENSOR_CAR_WALL: + return 300 + case Tid.TID_SENSOR_LINE: + return 301 + // Jacdac + case Tid.TID_SENSOR_SLIDER: + return 500 + case Tid.TID_SENSOR_ROTARY: + return 501 + case Tid.TID_SENSOR_LIGHT: + return 502 + case Tid.TID_SENSOR_ROTARY: + return 503 + + case Tid.TID_ACTUATOR_PAINT: + return 10 + case Tid.TID_ACTUATOR_SHOW_NUMBER: + return 15 + case Tid.TID_ACTUATOR_SPEAKER: + return 20 + case Tid.TID_ACTUATOR_MUSIC: + return 22 + case Tid.TID_ACTUATOR_RADIO_SEND: + return 100 + case Tid.TID_ACTUATOR_RADIO_SET_GROUP: + return 105 + case Tid.TID_ACTUATOR_SWITCH_PAGE: + return 110 + case Tid.TID_ACTUATOR_CUP_X_ASSIGN: + return 200 + case Tid.TID_ACTUATOR_CUP_Y_ASSIGN: + return 201 + case Tid.TID_ACTUATOR_CUP_Z_ASSIGN: + return 202 + // car + case Tid.TID_ACTUATOR_CAR: + return 500 + // jacdac + case Tid.TID_ACTUATOR_RGB_LED: + return 600 + case Tid.TID_ACTUATOR_SERVO_SET_ANGLE: + return 601 + } + return 1000 + } + + const only5 = [ + Tid.TID_FILTER_COIN_1, + Tid.TID_FILTER_COIN_2, + Tid.TID_FILTER_COIN_3, + Tid.TID_FILTER_COIN_4, + Tid.TID_FILTER_COIN_5, + ] + + export function getConstraints(tile: Tile): Constraints { + const tid = getTid(tile) + switch (tid) { + case Tid.TID_SENSOR_PRESS: + case Tid.TID_SENSOR_RELEASE: + return { allow: ["press_event"] } + case Tid.TID_SENSOR_START_PAGE: + return { allow: ["timespan"] } + case Tid.TID_SENSOR_CUP_X_WRITTEN: + return { + allow: ["value_in"], + disallow: [Tid.TID_FILTER_CUP_X_READ], + } + case Tid.TID_SENSOR_CUP_Y_WRITTEN: + return { + allow: ["value_in"], + disallow: [Tid.TID_FILTER_CUP_Y_READ], + } + case Tid.TID_SENSOR_CUP_Z_WRITTEN: + return { + allow: ["value_in"], + disallow: [Tid.TID_FILTER_CUP_Z_READ], + } + case Tid.TID_SENSOR_RADIO_RECEIVE: + return { + allow: ["value_in"], + provides: [Tid.TID_SENSOR_RADIO_RECEIVE], + } + case Tid.TID_SENSOR_SLIDER: + case Tid.TID_SENSOR_CAR_WALL: + case Tid.TID_SENSOR_MAGNET: + case Tid.TID_SENSOR_LIGHT: + case Tid.TID_SENSOR_LED_LIGHT: + return { allow: only5 } + case Tid.TID_SENSOR_MICROPHONE: + return { allow: only5.concat([Tid.TID_FILTER_LOUD]) } + case Tid.TID_SENSOR_TEMP: + return { allow: ["temperature_event"] } + case Tid.TID_SENSOR_ROTARY: + return { allow: ["rotary_event"] } + case Tid.TID_SENSOR_LINE: + return { allow: ["line"] } + case Tid.TID_SENSOR_TIMER: + return { allow: ["timespan"] } + case Tid.TID_SENSOR_ACCELEROMETER: + return { allow: ["accel_event"] } + case Tid.TID_ACTUATOR_PAINT: + return { allow: ["icon_editor", "loop"] } + case Tid.TID_ACTUATOR_SPEAKER: + return { allow: ["sound_emoji", "loop"] } + case Tid.TID_ACTUATOR_MUSIC: + return { allow: ["melody_editor", "loop"] } + case Tid.TID_ACTUATOR_RADIO_SEND: + case Tid.TID_ACTUATOR_SHOW_NUMBER: + case Tid.TID_ACTUATOR_CUP_X_ASSIGN: + case Tid.TID_ACTUATOR_CUP_Y_ASSIGN: + case Tid.TID_ACTUATOR_CUP_Z_ASSIGN: + return { allow: ["value_out", "constant"] } + case Tid.TID_ACTUATOR_RGB_LED: + return { allow: ["rgb_led", "loop"] } + case Tid.TID_ACTUATOR_SERVO_SET_ANGLE: + case Tid.TID_ACTUATOR_RADIO_SET_GROUP: + case Tid.TID_MODIFIER_LOOP: + return { only: ["constant"] } // ahy only and not allow? + case Tid.TID_ACTUATOR_SWITCH_PAGE: + return { allow: ["page"] } + case Tid.TID_ACTUATOR_CAR: + return { allow: ["car"] } + case Tid.TID_MODIFIER_RADIO_VALUE: + return { requires: [Tid.TID_SENSOR_RADIO_RECEIVE] } + case Tid.TID_MODIFIER_RANDOM_TOSS: + return { allow: ["constant"], disallow: ["value_out"] } + } + return undefined + } + + export function getCategory(tile: Tile): string { + const tid = getTid(tile) + if (isPressReleaseEvent(tid)) return "press_event" + if (isLineEvent(tid)) return "line" + if (isTimespan(tid)) return "timespan" + if (isAccelerometerEvent(tid)) return "accel_event" + if (isEmoji(tid)) return "sound_emoji" + if (isFilterConstant(tid) || isFilterVariable(tid)) return "value_in" + if (isModifierConstant(tid)) return "constant" + if (isModifierVariable(tid)) return "value_out" + if (isPage(tid)) return "page" + if (isCarModifier(tid)) return "car" + if (isLedModifier(tid)) return "rgb_led" + switch (tid) { + case Tid.TID_FILTER_ROTARY_LEFT: + case Tid.TID_FILTER_ROTARY_RIGHT: + return "rotary_event" + case Tid.TID_FILTER_TEMP_WARMER: + case Tid.TID_FILTER_TEMP_COLDER: + return "temperature_event" + case Tid.TID_FILTER_LOUD: + case Tid.TID_FILTER_QUIET: // dead + return "sound_event" + case Tid.TID_MODIFIER_LOOP: + return "loop" + case Tid.TID_MODIFIER_ICON_EDITOR: + return "icon_editor" + case Tid.TID_MODIFIER_MELODY_EDITOR: + return "melody_editor" + case Tid.TID_MODIFIER_RANDOM_TOSS: + case Tid.TID_MODIFIER_TEMP_READ: + case Tid.TID_MODIFIER_RADIO_VALUE: + return "value_out" + } + return undefined + } + + // following functions are needed to compile to JacScript + + // let P be jdParam + export enum JdKind { + Literal = 1, // value is P + Variable, // value is variables[P] + Page, // value is page[P] + EventCode, + ServiceInstanceIndex, + ServiceCommandArg, // argument of command sent will be set to P; P2 is duration in ms for Sequance + ExtLibFn, // call external function P(P2) + Timespan, + RadioValue, + Rotary, + Temperature, + + Loop, // repeat modifier + + // Filter/actuator kinds + Radio, // radio send/recv + RandomToss, // random number + NumFmt, // on actuator - P is numfmt + + // for each modifier (defaults to [defaultModifier]), do ... + // P is a shortcut external function + // P2 is service arg size + Sequence, + } + + export function jdKind(tile: Tile): JdKind { + const tid = getTid(tile) + if (isPressReleaseEvent(tid)) return JdKind.ServiceInstanceIndex + if ( + isLineEvent(tid) || + isFilterConstant(tid) || + isModifierConstant(tid) + ) + return JdKind.Literal + if (isTimespan(tid)) return JdKind.Timespan + if ( + isEmoji(tid) || + tid == Tid.TID_MODIFIER_ICON_EDITOR || + tid == Tid.TID_MODIFIER_MELODY_EDITOR + ) + return JdKind.ServiceCommandArg + if (isPage(tid)) return JdKind.Page + if (isLedModifier(tid)) return JdKind.ExtLibFn + if (isCarModifier(tid)) return JdKind.NumFmt + switch (tid) { + case Tid.TID_MODIFIER_LOOP: + return JdKind.Loop + case Tid.TID_SENSOR_RADIO_RECEIVE: + case Tid.TID_SENSOR_CAR_WALL: + case Tid.TID_SENSOR_LINE: + return JdKind.Radio + case Tid.TID_MODIFIER_RADIO_VALUE: + return JdKind.RadioValue + case Tid.TID_SENSOR_TEMP: + case Tid.TID_MODIFIER_TEMP_READ: + return JdKind.Temperature + case Tid.TID_MODIFIER_RANDOM_TOSS: + return JdKind.RandomToss + case Tid.TID_SENSOR_ROTARY: + return JdKind.Rotary + case Tid.TID_FILTER_ROTARY_LEFT: + case Tid.TID_FILTER_ROTARY_RIGHT: + case Tid.TID_FILTER_TEMP_WARMER: + case Tid.TID_FILTER_TEMP_COLDER: + case Tid.TID_FILTER_ACCEL_SHAKE: + case Tid.TID_FILTER_ACCEL_TILT_UP: + case Tid.TID_FILTER_ACCEL_TILT_DOWN: + case Tid.TID_FILTER_ACCEL_TILT_LEFT: + case Tid.TID_FILTER_ACCEL_TILT_RIGHT: + case Tid.TID_FILTER_ACCEL_FACE_DOWN: + case Tid.TID_FILTER_ACCEL_FACE_UP: + case Tid.TID_FILTER_LOUD: + case Tid.TID_FILTER_QUIET: + return JdKind.EventCode + case Tid.TID_ACTUATOR_PAINT: + case Tid.TID_ACTUATOR_SPEAKER: + case Tid.TID_ACTUATOR_MUSIC: + case Tid.TID_ACTUATOR_RGB_LED: + case Tid.TID_ACTUATOR_CAR: + return JdKind.Sequence + case Tid.TID_ACTUATOR_SHOW_NUMBER: + return JdKind.ExtLibFn + case Tid.TID_ACTUATOR_RADIO_SEND: + case Tid.TID_ACTUATOR_RADIO_SET_GROUP: + case Tid.TID_ACTUATOR_SERVO_SET_ANGLE: + return JdKind.NumFmt + case Tid.TID_SENSOR_CUP_X_WRITTEN: + case Tid.TID_SENSOR_CUP_Y_WRITTEN: + case Tid.TID_SENSOR_CUP_Z_WRITTEN: + case Tid.TID_ACTUATOR_CUP_X_ASSIGN: + case Tid.TID_ACTUATOR_CUP_Y_ASSIGN: + case Tid.TID_ACTUATOR_CUP_Z_ASSIGN: + case Tid.TID_FILTER_CUP_X_READ: + case Tid.TID_FILTER_CUP_Y_READ: + case Tid.TID_FILTER_CUP_Z_READ: + case Tid.TID_MODIFIER_CUP_X_READ: + case Tid.TID_MODIFIER_CUP_Y_READ: + case Tid.TID_MODIFIER_CUP_Z_READ: + return JdKind.Variable + } + return undefined + } + + export function jdParam(tile: Tile): any { + const tid = getTid(tile) + if (isModifierConstant(tid)) return tid - Tid.TID_MODIFIER_COIN_1 + 1 + if (isFilterConstant(tid)) return tid - Tid.TID_FILTER_COIN_1 + 1 + if (isPage(tid)) return tid - Tid.TID_MODIFIER_PAGE_1 + 1 + if (isLedColor(tid)) return "led_solid" + if (isCarModifier(tid)) return jacs.NumFmt.F64 + switch (tid) { + case Tid.TID_FILTER_BUTTON_A: + return 0 + case Tid.TID_FILTER_BUTTON_B: + return 1 + case Tid.TID_FILTER_LOGO: + return 2 + case Tid.TID_FILTER_PIN_0: + return 3 + case Tid.TID_FILTER_PIN_1: + return 4 + case Tid.TID_FILTER_PIN_2: + return 5 + case Tid.TID_FILTER_KITA_KEY_1: + return 6 + case Tid.TID_FILTER_KITA_KEY_2: + return 7 + // + case Tid.TID_SENSOR_CUP_X_WRITTEN: + case Tid.TID_ACTUATOR_CUP_X_ASSIGN: + case Tid.TID_FILTER_CUP_X_READ: + case Tid.TID_MODIFIER_CUP_X_READ: + return 0 + case Tid.TID_SENSOR_CUP_Y_WRITTEN: + case Tid.TID_ACTUATOR_CUP_Y_ASSIGN: + case Tid.TID_FILTER_CUP_Y_READ: + case Tid.TID_MODIFIER_CUP_Y_READ: + return 1 + case Tid.TID_SENSOR_CUP_Z_WRITTEN: + case Tid.TID_ACTUATOR_CUP_Z_ASSIGN: + case Tid.TID_FILTER_CUP_Z_READ: + case Tid.TID_MODIFIER_CUP_Z_READ: + return 2 + // + case Tid.TID_FILTER_ROTARY_LEFT: + case Tid.TID_FILTER_TEMP_WARMER: + case Tid.TID_FILTER_LOUD: + return 1 + // + case Tid.TID_FILTER_ROTARY_RIGHT: + case Tid.TID_FILTER_TEMP_COLDER: + case Tid.TID_FILTER_QUIET: + return 2 + // + case Tid.TID_FILTER_LINE_BOTH: + return robot.robots.RobotCompactCommand.LineBoth + case Tid.TID_FILTER_LINE_LEFT: + return robot.robots.RobotCompactCommand.LineLeft + case Tid.TID_FILTER_LINE_RIGHT: + return robot.robots.RobotCompactCommand.LineRight + case Tid.TID_FILTER_LINE_NEITHER: + return robot.robots.RobotCompactCommand.LineNone + case Tid.TID_FILTER_LINE_NEITHER_LEFT: + return robot.robots.RobotCompactCommand.LineLostLeft + case Tid.TID_FILTER_LINE_NEITHER_RIGHT: + return robot.robots.RobotCompactCommand.LineLostRight + // + case Tid.TID_FILTER_TIMESPAN_SHORT: + return 250 + case Tid.TID_FILTER_TIMESPAN_LONG: + return 1000 + case Tid.TID_FILTER_TIMESPAN_VERY_LONG: + return 5000 + case Tid.TID_FILTER_TIMESPAN_RANDOM: + return -1000 + // + case Tid.TID_FILTER_ACCEL_SHAKE: + return 0x8b + case Tid.TID_FILTER_ACCEL_TILT_UP: + return 0x81 + case Tid.TID_FILTER_ACCEL_TILT_DOWN: + return 0x82 + case Tid.TID_FILTER_ACCEL_TILT_LEFT: + return 0x83 + case Tid.TID_FILTER_ACCEL_TILT_RIGHT: + return 0x84 + case Tid.TID_FILTER_ACCEL_FACE_UP: + return 0x85 + case Tid.TID_FILTER_ACCEL_FACE_DOWN: + return 0x86 + // + case Tid.TID_ACTUATOR_PAINT: + return "dot_animation" + case Tid.TID_ACTUATOR_SHOW_NUMBER: + return "dot_showNumber" + case Tid.TID_ACTUATOR_MUSIC: + return "note_sequence" + // + case Tid.TID_ACTUATOR_RADIO_SEND: + return jacs.NumFmt.F64 + case Tid.TID_ACTUATOR_RADIO_SET_GROUP: + return jacs.NumFmt.U8 + // + case Tid.TID_MODIFIER_EMOJI_GIGGLE: + return "giggle" + case Tid.TID_MODIFIER_EMOJI_HAPPY: + return "happy" + case Tid.TID_MODIFIER_EMOJI_HELLO: + return "hello" + case Tid.TID_MODIFIER_EMOJI_MYSTERIOUS: + return "mysterious" + case Tid.TID_MODIFIER_EMOJI_SAD: + return "sad" + case Tid.TID_MODIFIER_EMOJI_SLIDE: + return "slide" + case Tid.TID_MODIFIER_EMOJI_SOARING: + return "soaring" + case Tid.TID_MODIFIER_EMOJI_SPRING: + return "spring" + case Tid.TID_MODIFIER_EMOJI_TWINKLE: + return "twinkle" + case Tid.TID_MODIFIER_EMOJI_YAWN: + return "yawn" + // + case Tid.TID_ACTUATOR_SERVO_SET_ANGLE: + return jacs.NumFmt.I32 + // + case Tid.TID_MODIFIER_RGB_LED_COLOR_SPARKLE: + return "led_anim_sparkle" + case Tid.TID_MODIFIER_RGB_LED_COLOR_RAINBOW: + return "led_anim_rainbow" + } + return undefined + } + + export function jdParam2(tile: Tile): number { + const tid = getTid(tile) + switch (tid) { + // length of the melody (milliseconds) + case Tid.TID_MODIFIER_EMOJI_GIGGLE: + return 1478 + case Tid.TID_MODIFIER_EMOJI_HAPPY: + return 1233 + case Tid.TID_MODIFIER_EMOJI_HELLO: + return 547 + case Tid.TID_MODIFIER_EMOJI_MYSTERIOUS: + return 4794 + case Tid.TID_MODIFIER_EMOJI_SAD: + return 1687 + case Tid.TID_MODIFIER_EMOJI_SLIDE: + return 1315 + case Tid.TID_MODIFIER_EMOJI_SOARING: + return 8192 + case Tid.TID_MODIFIER_EMOJI_SPRING: + return 2083 + case Tid.TID_MODIFIER_EMOJI_TWINKLE: + return 6772 + case Tid.TID_MODIFIER_EMOJI_YAWN: + return 2816 + case Tid.TID_ACTUATOR_PAINT: + return 5 + case Tid.TID_ACTUATOR_MUSIC: + return 6 + + case Tid.TID_MODIFIER_CAR_FORWARD: + return robot.robots.RobotCompactCommand.MotorRunForward + case Tid.TID_MODIFIER_CAR_REVERSE: + return robot.robots.RobotCompactCommand.MotorRunBackward + case Tid.TID_MODIFIER_CAR_TURN_LEFT: + return robot.robots.RobotCompactCommand.MotorTurnLeft + case Tid.TID_MODIFIER_CAR_TURN_RIGHT: + return robot.robots.RobotCompactCommand.MotorTurnRight + case Tid.TID_MODIFIER_CAR_STOP: + return robot.robots.RobotCompactCommand.MotorStop + case Tid.TID_MODIFIER_CAR_FORWARD_FAST: + return robot.robots.RobotCompactCommand.MotorRunForwardFast + case Tid.TID_MODIFIER_CAR_SPIN_LEFT: + return robot.robots.RobotCompactCommand.MotorSpinLeft + case Tid.TID_MODIFIER_CAR_SPIN_RIGHT: + return robot.robots.RobotCompactCommand.MotorSpinRight + case Tid.TID_MODIFIER_CAR_LED_COLOR_1: + return robot.robots.RobotCompactCommand.LEDRed + case Tid.TID_MODIFIER_CAR_LED_COLOR_2: + return robot.robots.RobotCompactCommand.LEDGreen + case Tid.TID_MODIFIER_CAR_LED_COLOR_3: + return robot.robots.RobotCompactCommand.LEDBlue + case Tid.TID_MODIFIER_CAR_LED_COLOR_4: + return robot.robots.RobotCompactCommand.LEDOff + case Tid.TID_MODIFIER_CAR_ARM_OPEN: + return robot.robots.RobotCompactCommand.ArmOpen + case Tid.TID_MODIFIER_CAR_ARM_CLOSE: + return robot.robots.RobotCompactCommand.ArmClose + + case Tid.TID_MODIFIER_RGB_LED_COLOR_1: + return 0x2f0000 + case Tid.TID_MODIFIER_RGB_LED_COLOR_2: + return 0x002f00 + case Tid.TID_MODIFIER_RGB_LED_COLOR_3: + return 0x00002f + case Tid.TID_MODIFIER_RGB_LED_COLOR_4: + return 0x2f002f + case Tid.TID_MODIFIER_RGB_LED_COLOR_5: + return 0x2f2f00 + case Tid.TID_MODIFIER_RGB_LED_COLOR_6: + return 0x000000 + case Tid.TID_MODIFIER_ICON_EDITOR: + return 400 // ms + case Tid.TID_MODIFIER_MELODY_EDITOR: + return 250 // ms + } + return undefined + } + + // Jacdac event codes + export function eventCode(tile: Tile) { + const tid = getTid(tile) + switch (tid) { + case Tid.TID_SENSOR_TEMP: + case Tid.TID_FILTER_QUIET: + case Tid.TID_SENSOR_RELEASE: + return 2 + case Tid.TID_SENSOR_LINE: + case Tid.TID_SENSOR_CAR_WALL: + case Tid.TID_SENSOR_RADIO_RECEIVE: + return 0x91 + case Tid.TID_SENSOR_MICROPHONE: + case Tid.TID_SENSOR_ROTARY: + case Tid.TID_FILTER_LOUD: + case Tid.TID_SENSOR_PRESS: + return 1 + case Tid.TID_SENSOR_ACCELEROMETER: + return 0x8b + default: + return undefined + } + } + + export function jdExternalClass(tile: Tile) { + const tid = getTid(tile) + switch (tid) { + case Tid.TID_FILTER_KITA_KEY_1: + case Tid.TID_FILTER_KITA_KEY_2: + return jacs.ServiceClass.Button + case Tid.TID_SENSOR_SLIDER: + return jacs.ServiceClass.Potentiometer + case Tid.TID_SENSOR_MAGNET: + return jacs.ServiceClass.MagneticFieldLevel + case Tid.TID_SENSOR_LIGHT: + return jacs.ServiceClass.LightLevel + case Tid.TID_SENSOR_ROTARY: + return jacs.ServiceClass.RotaryEncoder + case Tid.TID_ACTUATOR_RGB_LED: + return jacs.ServiceClass.Led + case Tid.TID_ACTUATOR_SERVO_SET_ANGLE: + return jacs.ServiceClass.Servo + default: + return undefined + } + } + + export function serviceClassName(tile: Tile): jacs.ServiceClass { + const tid = getTid(tile) + switch (tid) { + case Tid.TID_SENSOR_PRESS: + case Tid.TID_SENSOR_RELEASE: + return jacs.ServiceClass.Button + case Tid.TID_SENSOR_TEMP: + return jacs.ServiceClass.Temperature + case Tid.TID_SENSOR_RADIO_RECEIVE: + case Tid.TID_ACTUATOR_RADIO_SEND: + case Tid.TID_ACTUATOR_RADIO_SET_GROUP: + case Tid.TID_SENSOR_LINE: + case Tid.TID_SENSOR_CAR_WALL: + case Tid.TID_ACTUATOR_CAR: + return jacs.ServiceClass.Radio + case Tid.TID_SENSOR_SLIDER: + return jacs.ServiceClass.Potentiometer + case Tid.TID_SENSOR_MAGNET: + return jacs.ServiceClass.MagneticFieldLevel + case Tid.TID_SENSOR_LIGHT: + case Tid.TID_SENSOR_LED_LIGHT: + return jacs.ServiceClass.LightLevel + case Tid.TID_SENSOR_ROTARY: + return jacs.ServiceClass.RotaryEncoder + case Tid.TID_SENSOR_ACCELEROMETER: + return jacs.ServiceClass.Accelerometer + case Tid.TID_SENSOR_MICROPHONE: + return jacs.ServiceClass.SoundLevel + case Tid.TID_ACTUATOR_PAINT: + case Tid.TID_ACTUATOR_SHOW_NUMBER: + return jacs.ServiceClass.DotMatrix + case Tid.TID_ACTUATOR_SPEAKER: + return jacs.ServiceClass.SoundPlayer + case Tid.TID_ACTUATOR_MUSIC: + return jacs.ServiceClass.Buzzer + case Tid.TID_ACTUATOR_RGB_LED: + return jacs.ServiceClass.Led + case Tid.TID_ACTUATOR_SERVO_SET_ANGLE: + return jacs.ServiceClass.Servo + default: + return undefined + } + } + + export function serviceCommand(tile: Tile) { + const tid = getTid(tile) + switch (tid) { + case Tid.TID_ACTUATOR_PAINT: + case Tid.TID_ACTUATOR_RGB_LED: + case Tid.TID_ACTUATOR_SERVO_SET_ANGLE: + return jacs.CMD_SET_REG | 0x2 + case Tid.TID_ACTUATOR_SPEAKER: + case Tid.TID_ACTUATOR_MUSIC: + return 0x80 + case Tid.TID_ACTUATOR_CAR: + case Tid.TID_ACTUATOR_RADIO_SEND: + return 0x81 + case Tid.TID_ACTUATOR_RADIO_SET_GROUP: + return jacs.CMD_SET_REG | 0x80 + default: + return undefined + } + } + + export function serviceCommandArg(tile: Tile): string | Buffer { + if (tile instanceof ModifierEditor) return tile.serviceCommandArg() + const ret = jdParam(tile) + if (typeof ret == "string") return ret + return undefined + } + + export function serviceIndex(tile: Tile) { + const tid = getTid(tile) + // these are special cases where we have multiple + // instances of the same service + if (tid == Tid.TID_SENSOR_LIGHT) return 1 + // default index is 0 + return 0 + } +} diff --git a/libs/microcode/tooltips.ts b/libs/microcode/tooltips.ts new file mode 100644 index 00000000000..e6848cadd1c --- /dev/null +++ b/libs/microcode/tooltips.ts @@ -0,0 +1,153 @@ +// auto-generated, run 'node scripts/lochex.mjs' to refresh +namespace microcode { + export const lang = "en" + export const font = simage.font8 + export function resolveTooltip(id: string) { + let res: string = "" + if (!id) return id + else if (id === "tagline") res = "for micro:bit V2"; + else if (id === "sensors") res = "when..."; + else if (id === "actuators") res = "do..."; + else if (id === "when") res = "when"; + else if (id === "do") res = "do"; + else if (id === "connect") res = "connect"; + else if (id === "S1") res = "page start"; + else if (id === "S2") res = "press"; + else if (id === "S2B") res = "release"; + else if (id === "S3") res = "move"; + else if (id === "S4") res = "timer"; + else if (id === "S5") res = "light"; + else if (id === "S6") res = "temperature"; + else if (id === "S7") res = "radio receive"; + else if (id === "S8") res = "hear"; + else if (id === "S9A") res = "variable X set"; + else if (id === "S9B") res = "variable Y set"; + else if (id === "S9C") res = "variable Z set"; + else if (id === "S10") res = "magnet"; + else if (id === "S11") res = "slider"; + else if (id === "S12") res = "dial"; + else if (id === "rule") res = "rule"; + else if (id === "add_rule") res = "add rule"; + else if (id === "delete_rule") res = "delete rule"; + else if (id === "arith_equals") res = "equals"; + else if (id === "arith_plus") res = "plus"; + else if (id === "disk") res = "save"; + else if (id === "load") res = "load"; + else if (id === "F0") res = "touch pin 0"; + else if (id === "F1") res = "touch pin 1"; + else if (id === "F2") res = "touch pin 2"; + else if (id === "F3") res = "button A"; + else if (id === "F4") res = "button B"; + else if (id === "F5") res = "key 1"; + else if (id === "F6") res = "key 2"; + else if (id === "F7") res = "logo"; + else if (id === "F8") res = "1"; + else if (id === "F9") res = "2"; + else if (id === "F10") res = "3"; + else if (id === "F11") res = "4"; + else if (id === "F12") res = "5"; + else if (id === "F13") res = "1/4 second"; + else if (id === "F14") res = "1 second"; + else if (id === "F18") res = "1 random second"; + else if (id === "F19") res = "5 seconds"; + else if (id === "F15") res = "loud"; + else if (id === "F16") res = "quiet"; + else if (id === "F17_shake") res = "shake"; + else if (id === "F17_tilt_up") res = "tilt up"; + else if (id === "F17_tilt_down") res = "tilt down"; + else if (id === "F17_tilt_left") res = "tilt left"; + else if (id === "F17_tilt_right") res = "tilt right"; + else if (id === "F17_face_up") res = "face up"; + else if (id === "F17_face_down") res = "face down"; + else if (id === "F20A") res = "variable X"; + else if (id === "F20B") res = "variable Y"; + else if (id === "F20C") res = "variable Z"; + else if (id === "F21L") res = "turn left"; + else if (id === "F21R") res = "turn right"; + else if (id === "F22U") res = "warmer"; + else if (id === "F22D") res = "colder"; + else if (id === "C0") res = "edit"; + else if (id === "C1") res = "samples"; + else if (id === "A1") res = "switch page"; + else if (id === "A2") res = "play sound"; + else if (id === "A3") res = "microphone"; + else if (id === "A4") res = "music"; + else if (id === "A5") res = "show image"; + else if (id === "A6") res = "radio send"; + else if (id === "A6A") res = "radio set group"; + else if (id === "A7") res = "random number"; + else if (id === "A10") res = "show number"; + else if (id === "M1") res = "page 1"; + else if (id === "M2") res = "page 2"; + else if (id === "M3") res = "page 3"; + else if (id === "M4") res = "page 4"; + else if (id === "M5") res = "page 5"; + else if (id === "M6") res = "1"; + else if (id === "M7") res = "2"; + else if (id === "M8") res = "3"; + else if (id === "M9") res = "4"; + else if (id === "M10") res = "5"; + else if (id === "M15") res = "LED image"; + else if (id === "M18") res = "music"; + else if (id === "M19giggle") res = "giggle"; + else if (id === "M19happy") res = "happy"; + else if (id === "M19hello") res = "hello"; + else if (id === "M19mysterious") res = "mysterious"; + else if (id === "M19sad") res = "sad"; + else if (id === "M19slide") res = "slide"; + else if (id === "M19soaring") res = "soaring"; + else if (id === "M19spring") res = "spring"; + else if (id === "M19twinkle") res = "twinkle"; + else if (id === "M19yawn") res = "yawn"; + else if (id === "M20A") res = "variable X"; + else if (id === "M20B") res = "variable Y"; + else if (id === "M20C") res = "variable Z"; + else if (id === "M21") res = "radio value"; + else if (id === "M22") res = "dice"; + else if (id === "M23") res = "repeat"; + else if (id === "M24") res = "melody"; + else if (id === "M25") res = "temperature"; + else if (id === "A8") res = "LED"; + else if (id === "A9A") res = "set variable X"; + else if (id === "A9B") res = "set variable Y"; + else if (id === "A9C") res = "set variable Z"; + else if (id === "A20_1") res = "red"; + else if (id === "A20_2") res = "green"; + else if (id === "A20_3") res = "blue"; + else if (id === "A20_4") res = "purple"; + else if (id === "A20_5") res = "yellow"; + else if (id === "A20_6") res = "black"; + else if (id === "A20_rainbow") res = "rainbow"; + else if (id === "A20_sparkle") res = "sparkle"; + else if (id === "A21_") res = "servo set angle"; + else if (id === "SR_LED") res = "LED {x} {y} {state}"; + else if (id === "SR_NOTE") res = "note {index} {state}"; + else if (id === "SR_ON") res = "on"; + else if (id === "SR_OFF") res = "off"; + else if (id === "CAR") res = "robot"; + else if (id === "CAR1") res = "forward"; + else if (id === "CAR2") res = "reverse"; + else if (id === "CAR3") res = "turn left"; + else if (id === "CAR4") res = "turn right"; + else if (id === "CAR5") res = "stop"; + else if (id === "CAR6") res = "fast forward"; + else if (id === "CAR7") res = "spin left"; + else if (id === "CAR8") res = "spin right"; + else if (id === "CAR9") res = "LED red"; + else if (id === "CAR10") res = "LED green"; + else if (id === "CAR11") res = "LED blue"; + else if (id === "CAR12") res = "LED OFF"; + else if (id === "CAR13") res = "arm open"; + else if (id === "CAR14") res = "arm close"; + else if (id === "S13") res = "wall"; + else if (id === "S14") res = "line"; + else if (id === "S15") res = "light"; + else if (id === "F23L") res = "left"; + else if (id === "F23R") res = "right"; + else if (id === "F23B") res = "both"; + else if (id === "F23N") res = "none"; + else if (id === "F23NL") res = "lost left"; + else if (id === "F23NR") res = "lost right"; + return res + } +} \ No newline at end of file diff --git a/libs/microcode/utils.ts b/libs/microcode/utils.ts new file mode 100644 index 00000000000..6aeda7994fc --- /dev/null +++ b/libs/microcode/utils.ts @@ -0,0 +1,75 @@ +namespace microcode { + export function assert(cond: boolean, msg?: string) { + if (!cond) { + if (msg == null) msg = "Assertion failed" + console.debug(msg) + throw msg + } + } + + // use this to manage a buffer that may grow + export class BufferWriter { + private buf: Buffer + private ptr: number = 0 + + constructor() { + this.buf = Buffer.create(64) + } + + public get length() { + return this.ptr + } + + public get buffer() { + const buf = Buffer.create(this.ptr) + buf.write(0, this.buf.slice(0, this.ptr)) + return buf + } + + public writeByte(v: number) { + assert( + 0 <= v && v <= 0xff && (v | 0) == v, + "writeByte: v=" + v.toString() + ) + if (this.ptr >= this.buf.length) { + const copy = Buffer.create(this.buf.length * 2) + copy.write(0, this.buf) + this.buf = copy + } + this.buf[this.ptr++] = v + } + + public writeBuffer(b: Buffer) { + for (let i = 0; i < b.length; ++i) this.writeByte(b[i]) + } + } + + export class BufferReader { + constructor(private buf: Buffer, private ptr: number = 0) {} + + public get buffer() { + return this.buf + } + + public eof() { + return this.ptr >= this.buf.length + } + + public peekByte() { + assert(this.ptr < this.buf.length) + return this.buf[this.ptr] + } + + public readByte() { + assert(this.ptr < this.buf.length) + return this.buf[this.ptr++] + } + + public readBuffer(len: number) { + assert(this.ptr + len <= this.buf.length) + const b = Buffer.create(len) + for (let i = 0; i < len; ++i) b[i] = this.buf[this.ptr++] + return b + } + } +} diff --git a/libs/microcode/version.ts b/libs/microcode/version.ts new file mode 100644 index 00000000000..dacec9928cb --- /dev/null +++ b/libs/microcode/version.ts @@ -0,0 +1,8 @@ + +// Auto-generated file: do not edit. +namespace microcode { + /** + * Version of the package + */ + export const VERSION = "v2.5.30" +} \ No newline at end of file diff --git a/libs/screen/_locales/screen-jsdoc-strings.json b/libs/screen/_locales/screen-jsdoc-strings.json new file mode 100644 index 00000000000..f139786f020 --- /dev/null +++ b/libs/screen/_locales/screen-jsdoc-strings.json @@ -0,0 +1,46 @@ +{ + "SImage.blit": "Copy an image from a source rectangle to a destination rectangle, stretching or\ncompressing to fit the dimensions of the destination rectangle, if necessary.", + "SImage.blitRow": "Scale and copy a row of pixels from a texture.", + "SImage.clone": "Return a copy of the current image\n\nReturn a copy of the current image", + "SImage.copyFrom": "Sets all pixels in the current image from the other image, which has to be of the same size and\nbpp.", + "SImage.doubled": "Stretches the image in both directions by 100%", + "SImage.doubledX": "Stretches the image horizontally by 100%", + "SImage.doubledY": "Stretches the image vertically by 100%", + "SImage.drawCircle": "Draw a circle", + "SImage.drawIcon": "Draw an icon (monochromatic image) using given color", + "SImage.drawImage": "Draw given image on the current image", + "SImage.drawLine": "Draw a line", + "SImage.drawRect": "Draw an empty rectangle", + "SImage.drawTransparentImage": "Draw given image with transparent background on the current image", + "SImage.equals": "Returns true if the provided image is the same as this image,\notherwise returns false.", + "SImage.fill": "Fill entire image with a given color\n\nFill entire image with a given color", + "SImage.fillCircle": "Fills a circle", + "SImage.fillPolygon4": "Fills a 4-side-polygon", + "SImage.fillRect": "Fill a rectangle", + "SImage.fillTriangle": "Fills a triangle", + "SImage.flipX": "Flips (mirrors) pixels horizontally in the current image\n\nFlips (mirrors) pixels horizontally in the current image", + "SImage.flipY": "Flips (mirrors) pixels vertically in the current image\n\nFlips (mirrors) pixels vertically in the current image", + "SImage.getPixel": "Get a pixel color\n\nGet a pixel color", + "SImage.getRows": "Copy row(s) of pixel from image to buffer (8 bit per pixel).", + "SImage.height": "Get the height of the image", + "SImage.isMono": "True if the image is monochromatic (black and white)", + "SImage.mapRect": "Replace colors in a rectangle", + "SImage.overlapsWith": "Check if the current image \"collides\" with another", + "SImage.replace": "Replaces one color in an image with another\n\nReplaces one color in an image with another", + "SImage.rotated": "Returns an image rotated by -90, 0, 90, 180, 270 deg clockwise", + "SImage.scroll": "Every pixel in image is moved by (dx,dy)\n\nEvery pixel in image is moved by (dx,dy)", + "SImage.setPixel": "Set pixel color\n\nSet pixel color", + "SImage.setRows": "Copy row(s) of pixel from buffer to image.", + "SImage.transposed": "Returns a transposed image (with X/Y swapped)", + "SImage.width": "Get the width of the image", + "ScreenImage.brightness": "Gets current screen backlight brightness (0-100)", + "ScreenImage.setBrightness": "Sets the screen backlight brightness (10-100)", + "helpers.imageRotated": "Returns an image rotated by 90, 180, 270 deg clockwise", + "image.create": "Create new empty (transparent) image", + "image.doubledIcon": "Double the size of an icon", + "image.ofBuffer": "Create new image with given content", + "images": "Creation, manipulation and display of LED images.\n\nImage manipulation blocks", + "images._image": "An image", + "images._image|param|image": "the image", + "simage.screenImage": "Get the screen image" +} \ No newline at end of file diff --git a/libs/screen/_locales/screen-strings.json b/libs/screen/_locales/screen-strings.json new file mode 100644 index 00000000000..aeda63df0bb --- /dev/null +++ b/libs/screen/_locales/screen-strings.json @@ -0,0 +1,41 @@ +{ + "SImage.clone|block": "clone %picture=variables_get", + "SImage.drawLine|block": "draw line in %picture=variables_get from x %x0 y %y0 to x %x1 y %y1 %c=colorindexpicker", + "SImage.drawRect|block": "draw rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker", + "SImage.equals|block": "$this is equal to image $other", + "SImage.fillRect|block": "fill rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker", + "SImage.fill|block": "fill %picture=variables_get with %c=colorindexpicker", + "SImage.flipX|block": "flip %picture=variables_get horizontally", + "SImage.flipY|block": "flip %picture=variables_get vertically", + "SImage.getPixel|block": "%picture=variables_get color at x %x y %y", + "SImage.replace|block": "change color in %picture=variables_get from %from=colorindexpicker to %to=colorindexpicker", + "SImage.setPixel|block": "set %picture=variables_get color at x %x y %y to %c=colorindexpicker", + "helpers|block": "helpers", + "images._dialogImage|block": "%img", + "images._image|block": "$image", + "images._screenImage|block": "%img", + "images._spriteImage|block": "%img", + "images._tileImage|block": "%img", + "images._tileMapImage|block": "%img", + "images._tile|block": "%tile", + "images|block": "images", + "image|block": "image", + "simage.create|block": "create image width %width height %height", + "simage.screenImage|block": "screen", + "simage|block": "simage", + "{id:category}Control": "Control", + "{id:category}Helpers": "Helpers", + "{id:category}Image": "Image", + "{id:category}Images": "Images", + "{id:category}SImage": "SImage", + "{id:category}Scene": "Scene", + "{id:category}ScreenImage": "ScreenImage", + "{id:category}Simage": "Simage", + "{id:category}Texteffects": "Texteffects", + "{id:category}_helpers_workaround": "_helpers_workaround", + "{id:group}Compare": "Compare", + "{id:group}Create": "Create", + "{id:group}Drawing": "Drawing", + "{id:group}Tiles": "Tiles", + "{id:group}Transformations": "Transformations" +} \ No newline at end of file diff --git a/libs/screen/fieldeditors.ts b/libs/screen/fieldeditors.ts new file mode 100644 index 00000000000..e766f889ceb --- /dev/null +++ b/libs/screen/fieldeditors.ts @@ -0,0 +1,115 @@ +/** + * Image manipulation blocks + */ +//% weight=70 icon="\uf03e" color="#a5b1c2" +//% advanced=true +namespace images { + //% blockId=screen_image_picker block="%img" + //% shim=TD_ID + //% img.fieldEditor="sprite" + //% img.fieldOptions.taggedTemplate="img" + //% img.fieldOptions.decompileIndirectFixedInstances="true" + //% img.fieldOptions.decompileArgumentAsString="true" + //% img.fieldOptions.filter="!tile !dialog !background" + //% weight=100 group="Create" duplicateShadowOnDrag + //% help=images/sprite-image + export function _spriteImage(img: SImage) { + return img + } + + //% blockId=background_image_picker block="%img" + //% shim=TD_ID + //% img.fieldEditor="sprite" + //% img.fieldOptions.taggedTemplate="img" + //% img.fieldOptions.decompileIndirectFixedInstances="true" + //% img.fieldOptions.decompileArgumentAsString="true" + //% img.fieldOptions.sizes="-1,-1" + //% img.fieldOptions.filter="background" + //% weight=100 group="Create" + //% blockHidden=1 duplicateShadowOnDrag + export function _screenImage(img: SImage) { + return img + } + + //% blockId=tilemap_image_picker block="%img" + //% shim=TD_ID + //% img.fieldEditor="sprite" + //% img.fieldOptions.taggedTemplate="img" + //% img.fieldOptions.decompileIndirectFixedInstances="true" + //% img.fieldOptions.sizes="10,8;16,16;32,32;48,48;64,64;16,32;32,48;32,8;64,8;20,15;40,15" + //% weight=100 group="Create" + //% blockHidden=1 duplicateShadowOnDrag + export function _tileMapImage(img: SImage) { + return img + } + + //% blockId=tile_image_picker block="%img" + //% shim=TD_ID + //% img.fieldEditor="sprite" + //% img.fieldOptions.taggedTemplate="img" + //% img.fieldOptions.decompileIndirectFixedInstances="true" + //% img.fieldOptions.sizes="16,16;32,32;8,8" + //% img.fieldOptions.filter="tile" + //% weight=100 group="Create" + //% blockHidden=1 duplicateShadowOnDrag + export function _tileImage(img: SImage) { + return img + } + + //% blockId=tileset_tile_picker block="%tile" + //% shim=TD_ID + //% tile.fieldEditor="tileset" + //% tile.fieldOptions.decompileIndirectFixedInstances="true" + //% weight=10 blockNamespace="scene" group="Tiles" + //% blockHidden=1 duplicateShadowOnDrag + export function _tile(tile: SImage) { + return tile + } + + //% blockId=dialog_image_picker block="%img" + //% shim=TD_ID + //% img.fieldEditor="sprite" + //% img.fieldOptions.taggedTemplate="img" + //% img.fieldOptions.decompileIndirectFixedInstances="true" + //% img.fieldOptions.decompileArgumentAsString="true" + //% img.fieldOptions.sizes="15,15;18,18;21,21;24,24;9,9;12,12" + //% img.fieldOptions.filter="dialog" + //% weight=100 group="Create" + //% blockHidden=1 duplicateShadowOnDrag + export function _dialogImage(img: SImage) { + return img + } + + /** + * An image + * @param image the image + */ + //% blockId=image_picker block="$image" shim=TD_ID + //% image.fieldEditor="sprite" + //% image.fieldOptions.taggedTemplate="img" + //% image.fieldOptions.decompileIndirectFixedInstances="true" + //% image.fieldOptions.decompileArgumentAsString="true" + //% weight=0 group="Create" + //% help=images/image + export function _image(image: SImage): SImage { + return image; + } + + //% blockId=colorindexpicker block="%index" blockHidden=true shim=TD_ID + //% index.fieldEditor="colornumber" + //% index.fieldOptions.valueMode="index" + //% index.fieldOptions.decompileLiterals="true" + export function __colorIndexPicker(index: number) { + return index; + } + + /** + * A position picker + */ + //% blockId=positionPicker block="%index" blockHidden=true shim=TD_ID + //% index.fieldEditor="position" color="#ffffff" colorSecondary="#ffffff" + //% index.fieldOptions.decompileLiterals="true" + export function __positionPicker(index: number) { + return index; + } +} diff --git a/libs/screen/font12.jres b/libs/screen/font12.jres new file mode 100644 index 00000000000..809eba8bde5 --- /dev/null +++ b/libs/screen/font12.jres @@ -0,0 +1,6 @@ +{ + "simage.font12": { + "mimeType": "font/x-mkcd-b26", + "data": "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9A9UANwHAAD4AQAI/AcAADhSIAGqCOoIrAS4Aq4BqAi4CKwG6gGqACABOVJQBFQDVAjIB0wAUgFSBgAA/AkACP4PAAA6UgAEdAYUAf4PFAH0AgAA/AEACAAI/gcAADtSAAAkCbQErwJkAhQDxAQAAPwJAAj+DwAAPVIgAJAMiAakBaYEiAawCAAA+AkACP4HAABCUkQIVARUAzYANACsDyQAAAD8CQAI/wcAAENSAAToBKoCrAH4D64AuAcAAPwJAAj+BwAAR1IACP4FKgUqASoB/g0AAPwBAAgACP4HAABKUgAA9g9QAV4JUAn2BwAA/AEACAAI/gcAAE1SAADoD6oCrAqoCugHCADoAw4I6AcIAAAATlIAAFIEUgNMAOQPSgJqBAAA/AkACP4HAABRUhAAWAiEBRIE0wQEB8gCAAL8CAAI/gcAAFRSAAG+BOoCqg2qA6oIvgcAAPwJAAj+BwAAVlIkAKwPrASmBKQEvASsBwAA/AkACP4PAABbUgAA/A8MA+QDFAn8BwAA+AEACPwHAAAAAF1SAADAANgKVgrUB3QAzAYAAPgJAAj+BwAAY1IQCPAEvAbyAbQC5AwAAPwBAAgACP4HAABkUgAAJAisB5YCrAKkDyQAAAD8AQAI/g8AAGVSoAIqCioJ6g8qAb4CoAYAAPwJAAj+BwAAZ1LABz4AqgeqBPoEqgSuBwAA/AAACP8HAABpUogIbARsAgwB/A8MAWoCKAQAAPwJAAj+B2pSAAV8BTwFPgW8A/wBBAF0BQYFdAUEAwAAb1ICAN4PVgXWB1YFVgXeDwAA/AkACP4HAABwUgAArAT8AqwB/A+qAvoGAAD4AQAI/gcAAHJSjAC8D7QFtAX2BbQHtAeMBwAA/AEACP4HdVIQABAO0AP4BfQF5AXoBwAA+AEACPwHAAB/UgAIdgVwBXQD+g94A3ADdgUAAPgJAAj+B4NSSAT8B/wH/gf8B/wH/Af8BwAE+AEACPwHh1IADvgBGATYBn4DbA9sAggE8AEACPwPAACIUoAAfAj0CbQJtAX4A1QBXAn2CVwJVAcAAIlSAACcCcoLyQmwB84FQgUOBPgBAAj+BwAAjVIICOgErALqAEsIogbkBAAA/AEACP4HAACRUjgASAz0A4wClgK8AswPIAD8AQAI/gcAAJtSAAAQCBAEEAKQAX4AEAAQCBAIEAzwAwAAnVIABBQCZAHkABwJAASIA34ACAgICPgHAACeUgAAiAloBAgCiAF+CAgICAz4AwAA4AEAAJ9SBAIEAvwBBAkECRAEEAP+CBAIEAzwAwAAoFIICAgG/gEICAgI+AcAAPwPBAQEBPwPAAChUkAIUAlICSwFNgOkASQJNAlMCUQHQAAAAKNSKAioCKQIogSgA54AkAiICIIIhAcIAAAAqFIAACQE5AMkAqQCJAMACBAH/gAQCPAHAACpUgAE/gOSBJIC/gsABBAD8AgfCBAI8AcAAKpSAAhECVwJZgUkA1wBRAE0CSQJXAdEAAAAq1IAAEgHyAR+AkgCQAcQABAP/gAQCPAHAACxUgAO/AEUDPQDVAjUBwQAEAf8ABAI8AcAALJSAACkCKQElAesBKQEMAAQDv4BEAjwBwAAs1IACLQIlASWBJQD1ACUCJQIlgiUBzQAAAC0UgAIuAiKBIoEiALrAYgIiAiMCIoHOAAAALlSKAioBKgCDAGoAggIUAQQA/4IEAjwBwAAv1IAABQIVAl+BRQFwAM0AR4JBAk8B0AAIADBUgAAtASEBLQHhAS0DAQEEAP+CBAI8AcAAMNSAAA0AhQJXgeUATQJAAQQA/4IEAjwBwAAx1IACAIK+gqqBqoC+gKqAq4Kqgr6BgAAAADJUhAI+ASWA/QAnAfwCBgKmAl+CAgK+AkABMtSAAjuCSoEqgMqCO4LAAAQB/4AEAjwBwAA0lIAAOQCvgL0D74C5AgEBBAD/ggQCPAHAADVUgAAfAl8CfwHeg0IBBgCkAF+CBAI8AcAANhSgACCB/8FqgSqBP8FggwIAv8BCAT4AwAA2VIAACoDqgjqBzYA4AlIBy4BNAk0CSwHRAHdUgAM/gOSCP4HgAFqCTgHrgE4CW4JqgcAAd5SEgiSCKgErgSgAqIBpAioCKYIKAdyAAAA31IAAkQB/AV+A3wDfAF8BX4F/AN8AUQCAADiUgAArAmsCe4FrAWAA/gBGAkOCXgGgABgAORSAAj0CdYF9AfWBfQFAAwQA/4IEAjwBwAA51IAACAB7A+4B+gHqAeoDgAG/AEQCPAHAADyUgAB/g3+Af4B/g3+AQgLmAh+AAgB+AwAAPNSBAh9BW0B/wTsAvwCAAaIAX8ECAT4AwAA9VIADPwDzA/8AewD/AvMDwAM/gMQCPAHAAD4UgAB+g+7B4AHuwe6BwAM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JAAAAK1T+AcEAPQD/AJ8B3QFhAckBPwDJAEUAgAErlMABv4BAgT6AVoD+gECDPoDSgDKD0oAAACyUwAO/gECAOoP6gHqBeIH6gXqBeoJCg8CALNTAA74AQ4E+Ae4BroG/A8IAOgMLAPqBSgIu1NAAEgMSAZIBcgEfgRIBEgFSAZICEAAAAC/UwAAgACABP4GqgWqBKoEqgSqBP4GgASAAMFTIAGoCKgK7Aq6CqgKqgruCqgKqAggAQAAwlMgASgBqAhsCroKqAooCWoErASgACABAADDUwAAAAGwAKQJdgulChQKJgl2BKAEMAFAAchTAAAACAQIHARkBIQCBAGEAmQEHAQECAAIyVMACAQIPAREBJQCFAGUAkQEPAQEBAAIAADKUwAEBAaEAXwIJATEBAQDHAOQAnAEEAQAAMtTAAQIBAgDyAh+BKgEKAMoA6gEaAQICAAEzFMUCDQERALkARwKAAh8BIQDxAI8BAAIAADNUwAAAAz8AyQI5AgkBSQCJAakBWQIBAgAAM5TAAL4AQABAAH+DwAAFAjkBAQDxAQ8BAQI0VMABBACHAuQCPAIXgVQBlAF1ghQCBAIAADUUwACoAEgCP4PJACgCwQIfAaEAeQCHAwACNZTBAT8B5QElASUAvwPAABoCIgFCAPoBBgI11MAAHQIHATcBFQFXAJcAlIF2gQaBDAIAADYUwAIpAiUCIQJvAaGBIYEvAaECZQIJAgACNlTUAZYAVQI8g9UAFQDBAh8BIQDxAI8DAAA21MAAKwIoAb+AaQAAA78AXIMkgOSAnIMAATgUwAA6Ah2CFYPVg9GD1YPXg9WD3gIyAgAAOFTAACoANgHvwfaB/oHkAAMDPQChAF8BgAE4lMIAKoE7Af4B/wH6A/oAPwH7AXqB4gJgAjjUwAAAAD8DwQEBAQEBAQEBAQEBPwPAAAAAORTAAAIAIgPiASIBP4EiASIBIgEiA8IAAAA5VMAAGAAEADMByoCKAIoAugLCAgIDPgDAADmUwAAgAi8BKQEpAPkAKQApAikCLwIgAcAAOhTAAD8AwQB/AkACAQGhAF8BAQIBAz8AwAA6VMAAPwDBAEEAfwBAAD8DwQABAIEAvwBAADqUwAAAAh8BEQCRAFEAEQARAFEAnwEAAgAAOtTAAD8AwQBBAH8AQAA/AMAAQAB/g8AAAAA7FOAAEQARA8kCRQJ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ggCyAIoAAAB9VAAAEADQB1gCWALUAxIA1A9YAEgC0AEQAIZUAAD8AwQB/AEwAPgDlgSUBPQFBAX8BAACi1QAAPwDBAH8ARAACAAOAPgPKAEoASgBAACMVAAClAFUAPwHUgAAAPwHBAIEAgQC/AcAAI9UAAD8AwQB/AQAAtQBFAj0D4QAQAEgBgAEkFQAAPwDBAH8AWAA/A8CANAAEAkQCP4HEACSVAAIHAgUBNQDXABAAFwAVADUBxwIAAgABpVUAAD8AwQB/AEAAJAPkASQBP4EkASQDxAAllQAAPwHBAH8DAAC/gkIDPAD/A8EBPwPAACZVAAA/AMEAfwJAASQA34IEATyB5YIVAgQBJpUAAD8AwQB/AFQAEgCLgK1BDQFTAlEAEAAplQAAPwDBAH8AQAI7ASsAv4BrAKsBLwJAAinVAAA/AMEAfwBQAi8BJQDYAD4CQAI/gcAAKhUIAARAIoPoASsBKMEkgSOBJIEog8uAAAAqlQAAPwHBAL8AwAIrAcgAP4PIACoAyQMAACsVAAA/AMEAfwBAAgoCKgECgPMBQgEaAhACK9UAAD8AwQB/AEQAYgPVgllCXQJXAmED4AAsVQAAPwBBAH8AQAA+AeoAqwCqgKoAvgHAACyVAAA/AMEAfwJAAhOBEwC+AFMAk4ESggAALNUAAD8AwQB/AEACCQFtARvAiQClAVECAAAuFQADPgDCACoB6gCqAoIC/4ICAfKCCwIAAa9VAAA/AMEAfgA/g8CBCIF+gSiBQIE/g8AAMBUAAIEAXQBVA/UBFUE1gRUAVQCdAUEBQAIwVQAAMAPQAReBNIHEgDSD1IEXgRABMAPAADEVAAA/AMEAfwBAAiIBP4CiACIAP4CiAyACMZUAAD8AwQB/AEACCgJpASWBFQCTAHEAAAAx1QAAPwDBAH8AQAIJAkkCb4PJAkkCSAJAADIVAAA/AMEAfwBYAAwD6gEpgSoBDAPYABAAMlUEADUB1QCXgJUAtQJEAz+AhADkgRUCAAHzVQAAPwDBAH8AQAA+A8IAOgDLgHoCQgI+AfOVAAA/AMEAfwBAAgkCM4EBAMEBc4EJAgECNFUAAD8AwQB/AVABAQE/AcEBPwHBATkBAAA11QAAPwDBAH8AQAAfAECAZAPPgFIAUQBJADfVAAA/AMEAfwBAAT8BYIEEALOCAgI+AcAAOFUAAAACP8LrQatBq0CrQKtBq0G/wsACAAA5VQAAEQAfAdsBWwFbAVsB3wARAj8D0QAAADmVAAA/AMEAfwBAAj8D5IAEAz+A5AFVggABuhUAAD8AwQB/AEAAPYPUAFeAVAJUAn2BwAA6VT8AwQB/AEAAHwJVAlUCfwPVAlUCXwJAADqVAAA/AMEAfgMAgL+CZII/gf8DwIA/gMAAO1UgAieCJIIkgSeA8AAnAOUBNQE3AiACAAA7lQAAP4BggD+AIABagEqCb8PagEuASoBCQHyVAAAEgCyD78EigSgBJ4EiQSJBLkHCQAIAPpU/AMEAfwBAADoD6gCqAL+D6gCqgrsBwAA/FQAAPwBhAD8AAAAvAisCK4OrAGsAbwABAABVQAA/AcEA/wDAACoDqgKqgqqCqgKqA4IAAZVAAD8AwQB/AlACCgJngaIBPwGzAVYCEAIB1UAAAAP/gAiDuoKqgpqCqoKqgoqDSIBAAAJVQAA/AMEAfwBAAgoCS4F6gMoBS4JKAkAABBVAAb8AQQAXAdcBVwF/gVcBVwFfAcUAAAAFFUAAPwDBAH8AQAAqge6BK4EqgS6BKIHIAAgVQAA/AMEAfwBAAiUBJYC1AmUCJYMNAMAACRV/AMEAfwBAAj4BJQClgH0AZwClAT0BIAIJ1UAAPwDBAH8AQAE/AdUAjwA+A8EAPwDAAAsVQAA/AMEAfwBAAz4AygIPgdsAGwHTAgABC5VCAAIAHwPXgVcBVwFfgVcBVwFXA9EAAAAL1UAAPwDBAH8AQAA/A+qBKgE/geoBKgEAAAxVQAA/AMEAfwBAAD+B2oFagVqBWoF/gcAADdVAAD8AwQB/AEACPQH7ADmAmUKZAr0ByQAOFUAAPwDBAH8CAAGKACkBLIJNAr0ACQGCAg+VQAA/AMEAfwBAAjUChQK/A8UCtIKUAoQCENVAAD+AYIA/gAIAO4PqAKvAqoKqgrqDwAARFUAAPwDBAH8ASAElAJUCiwJxAdkAJQDAARGVQAAAAD0DxQAfAcWBTQFVAVcBxQA9A8AAEpVAAD8AwQB+AD+DzIAzAHkAxQB5Aj8BwQAT1UAAP4PKgCqB74CgAK+AqoDKggqCP4HAABTVcIAOgCqB6oEqgS6BJAEzgS0BDQHTACEAF9VAAb8AaoHqgKqAroDIAR+AogB6AIYBAAAYVUAAPwDBAH8AQAAKAH+DwAA/g8oASgBAABkVfwDBAH8AQAAfALUAtQCfgJUD1QCfAIAAGVVAAD8AwQB/AEQAEgHRAXyBVQFRAVIB1AAZlUAAPwDBAH8AQAM/gMICGAIiQkKDugJAABqVQAA/AMEAfwBgAj+B0AA+A+MBIoE+A8AAHBVAAD8AwQB+AmOCVIIfgVSAl4B0gFeAAAAeFX8AwQB/AEADKwDLAYsAf4PLAEsArwPCAB8VQAA/AMEAfwBAABsB6QApg+0AKQEpAMAAIBVAAD8BwQC/AMAAKQPtAlWCVQJpA6MAAAAglX8AwQB/AEAAPwA1A/UCPwJ1ALUB/wJAACDVQAA/AMEAfwA4A8UALQCngfUAhQI9AcAAIRVAAFEAVQPVgtUC/wLVAtUC9YL1A9EAQAAh1UAAPwDBAH8DGQCng/0AZQCYAj4CP4HAACJVQAA/AMEAfwBYAD4DwYAUAlUBdQDXAVQCYpVAAD8AwQB/AzwAwgHqAIIDX4CiAVqCAAGlFUAAPwDBAH8DAAC/Ak0CPQKtA+0CrwKAACYVfwDBAH8AQAAvA+wALAP/gCwD7AAvA8AAJpVAAD8AYQAfAjwBVgFNgOUAzwFUAXwCQABnFUAAQQBfA/cC1wLXgtcC1wL3At8DwQBAAGdVQAA/AMEAfwBAAC+BqoEqgWqCKoIvgcAAKdVAAD8AwQB/AEICPQLtAq2CrQK1AscCAAAqlUAAIQIvAisD7wIhAj+CYQCvASsCrwKhAirVQAA/AMEAfwJAAg+BTQDwAM8BQQJfAkAAKxVKAAsAJwP/ADcB9wF3AXcBfwHmgioDyAArlUAAAAE/gVaBVoF/g9QBV4FWgX6BQ4EAACyVQAA/gMCAf4BGAznApAAUAaOAAgI+AcAALNVAAD8AwQB/AEACNQLrAquCqwK1AsUCAAAtVX+AYIA/gAAAPAPkgSXBPIHkASXBPIHAgC2VQAAGAAKB3wFWAXeBVgFWAV8BQoHGAAAALdV/AMEAfwBAAjUC1QIVAZWAVQEVAjUCwAIu1UAAPwDBAH8ARgA2A+0AvIPlAEYCMgHEADFVQAA/AMEAfwBAAh8BVwFXgNeA9wFfAUACc5VAAD8AwQB/AEADP4AqgaqAP4EqgGCDwAA01UAAPwDBAH8AQAIUgV2A4YPlgNSBTAFAAjaVQAA/AMEAfwJAAT8ANQO1gDUBdwIwAcAANxVAAD8AwQB/AFAADQHfgfUB9wH1AfUBwAA4VX8AwQB/AEABMgCVAqiBxAAUgJ0CuQHCADjVQAA3A/UB9QP3A+EAVQC1AsECPwHAAAAAORV/AEEAfwBAATgB2wGeAbuB2gGaAbsDyAI5lUAAPwDBAH8AQAMVAH0CV4HFAGUBTAIAADoVQAA/AMEAfwAAA5EAJADzAKoC6gK6AcAAu9VAAD8AwQB+AA+AWICagRaBVoEQgF+AwAC/VUAAPwDBAH8BCQC/g9UAXQJEAbOAQgGOAgAVgAA/AMEAfwBAAD0DxQC9gVUBVwHFAj0BwFWAAD+AYIA/g74AYQE9AcUAIcEfAOFBnQCBlb8AwQB/AEACPQK1ArWBvQD1AbWCvQKAAAJVgAAgg6+Au4KrgqvBq4M7gquCr4Kgg4AAA5WAAD4AwgB8AUEBXwF/AN8A3wF/AV8BwQDF1YAAAwARg/WC/QL9gv0C/QL9gvFD5wAAAAbVgAA/AMEAfwI+AcEAfQPJgKEAfQPpAAEAx9WAAD8AwQB/ACAD34FUAWID/wHBAD8AwAAMVYAAPwBhAB8CAAH/A60AdwF/AfUAdwPAAAyVgAA/AMEAfwDfAKuD3wC/geSAP4PAAAAADRWAAD8AwQB/AEQBNgD7gL0B/AC7gLUBxAANlYAAPwDBAH4AAQN/gFUAf4N/AMkAOIPIgA5VgAA/gOCAf4BAAz6AVYJUw9WAfoBMgwAADtWAAD8AwQB/AEAAHwP3AteC1wL3At8DwQBP1b8AYQA/AAACF4FVgFSBf4JWgFSBV4AAA5BVgAA/AMEAfwAIA1UAdwHBAvcC1QJdAEADWJWAAD8AwQB+Al8CAwFZAN2ASQDRAV8CQAAaFYAAEACXA9UCdQJfA9gAEwP9Al0CVwPQAJpVgQIxAv8CvwLRAj8D0QI/AvsCvwLBAgAAGpWAAD8AwQB/AnABbwF9AOUD/QPvAPgBQAIdFYAAPwBhAD8BBAE9Af0AvYC9ALUBvQHEAiHVgAA/AMECfwH5As/COQPJA3kA/8PJACgA45WAAD8AwQB/AFACDwGvAa+A7wPvAPkBAAAo1YAAIgH7gWuBW4HWABuB24FrgVuBYgHAAC0VgAO4AG8BPQH9AfsD7gB1Al0BvwFYAgAALdWAAD8AwQB/AUADPwP7An+AfQH7AX8CQAAvFYAAPwDBAH8AQAA9A/yBfoMMgG5CLUHgADCVgAACAL+B/4H/gf+BvgA/gb+B/4HCAUAAclWAAD+AYIAfAH3BqUCBwP9D1cF9QdXBQAAylYABbQD/Av8B/wFngX8AfwD/AXcB5QHAADaVgAA/g8CBAIFggRCBDoEwgQCBQIE/g8AANtWAAD8DwQExAU8BAQEBAR8BIQEBAT8DwAA3lYAAP4PAgTyBJIEkgSSBPIEAgQCBP4PAADgVgAA/g8CBCIFIgWiBHoEogQiBQIE/g8AAOJWAAD+BwIEkgRSBFIFMgX6BRIEAgT+BwAA41YAAP4HAgQKBGoECgUKBfoECgQCBP4HAADkVv4PAgRSBJIEkgT6BZIGkgZSBgIE/g8AAO1WAAD+BwIEKgXqBCoE6gUqBSoFAgT+BwAA8FYAAP4PAgSiBWIE+gViBKIEIgUCBP4PAADxVgAA/A8EBEQFJAVXBZQEdAUUBQQE/A8AAPJWAAD+DwIEkgT6BZIEkgT6BZIEAgT+DwAA81YAAP4PAgZaB0IFygTCBDIFCgUCBP4PAAD0VgAA/g8CBKoEqgT6BaoEqgSKBQIE/g8AAPpWAAD+DwIEEgTSBVIFegVSBdIFAgT+DwAA/VYAAP4PAgQqBSoF+gUqBWoFKgUCBP4PAAD+VgAA/w8BBCkFLQUVBZUFrQYlBgEE/w8AAANXAAD8BwQE9AX0BPwE/AX0BPQE9AUEBPwHBlf+BwIG3gZWBlYFVgVWBF4GwgYCBP4HAAAIVwAA/gcCBD4E/gU6BroGegY6BQIE/gcAAAtXAAD+BwIE6gWqBUoEegWKBW4GAgT+BwAADVcAAP4HAgT+BX4FfgX+BX4FegUCBP4HAAAPVwAA/gcCBGoE6gX6BuoG+gZqBAIE/gcAABJXAAD+DwIM6g1qC3oLagtqC+oLAgz+DwAAE1cAAP4PAgjCC/oL6gvqC+oL+gsCCP4PAAAWVwAA/gcCBP4H9gf2B/YHfgbiBwIE/gcAABhX/g8CBP4E/gX+BP4E/gb+B/4EAgT+DwAAH1cABCAEIAQgBCAE/gcgBCAEIAQgBAAEAAAjV0AIRAhMCSwJNAmkDzQJNAlMCUQIQAgAACdXAAAADPwDBAhECEQI9A9ECEQIRAgECAAAKFcIAYgAyA84AI4IiAiICOgPiAiICIgIAAAtVwAIJAkkCSQJJAm/DyQJJAkkCSQJIAgAADBXAAIQAv4BEAFAAPwHIAggCP4JEAgQCfgEM1cABBAC/gMQAQAM/gMAAAAA/AcAAAAA/g86VwACEAL+AxABAAEiCfIEKgLmCSIM4AMAAD5XAAQQAv4DEAoABIQDfAiEBCQHPAXgBAAIQFcQAhAC/gEQAQAI8A8ACAAI/g8gCCAIIAhCVwACEAL/AZAEAAb+ATIM0gISA5ICcgQCCEdXAAIQAv4BEAEwAAwCKgEoCagICAz4AwAASlcQAhAC/gEQCQAMCAL4AUoISAhICMgHCABOVwACEAL+ARABQAg0CAoG6AEIAkgEOAgAAE9XAAIQAv4BEAFEAUQAJAD8DwQAJADEAIAAUFcgCJAIjgiwCIAI/g+gCJAIjgiQCCAIAABRVwACEAL+ARABAAzoAyoAKgDoBwgICAYAAFdXAAIQAf4BkACACAgESAP+AMgBSAJ4BEAIWlcACD4IAAkACX4JRg8qCRIJKgkmCUAIAABbVwACEAL+ARABAAQkBqQFZAQkBKQFJA4ACF1XAAIQAv4BEAEACP4JAgQCAvoBAgQCBP4JXlcQAhAC/gMQAQAC/AKEAoYCpgqkCJwHAABfVwACEAL+ARABgAkIBHgEigOMAngECAgIBGBXAAR+BQIFPgUCBaAHMAUOBRAFIAVABAAAYVcAAhAC/gMQCQAM+APICX4GSAbICVgIAABkVxAC/gEQAQAA/AMkASQB/w8kASQB/AMAAGZXgAAQAf4AkAAABP4EkgSSBJIEkgT+BAAEalcAABAC/gMQAQIAugCCAP4PggCyAIoAAABvVwAEEAL+AxACQAlECCQI/AskCEQIhAgAAHdXAAIQAv4DEAEEAPQDFAH0CQQI/AcEAAAAglcAAEACVAr0C1QKVAr8D1QK8gtSCkAAAACDVwACEAL+ARABAAQoBMgFCgQIB+gECAQAAIRXiAiICmgKGApMCkgPeAqsCqwKiApICAAAi1cAAEAEygQ+BAoFfgUKBwAFHgVABX4EAASSV0AIaAlICWwJCgkID2gJTAlICSgJQAgAAJtXAAQQBP4DEAIACKAEngLCD4IBvgKgBKAIolcAAhAC/gEQAQAO/AGUD1QEVARSBNIPEACjVwACEAL+AxAJBAj0C1QKVApUClQK9AsECKZXAAAACP4KqgoqCioPagqqCqoK3gqACoAIq1ckBJQF/gUUBQAF1Ac0BR4FBAV8BIAEQACuVwAEEAL8AxACgACoArgDrAqoCpgKqAaoAMJXAAIQAv4DEAkACfwHrAasBfwIrAisCPwAw1cABBAC/gMQAUAIKAkuBegDLAUsCSAJAADLVwACEAL+ARABAAh8CVQJVAn8D1QJVAl8Cc5XAAAQAv4BAAz4AygAKArICX4EiAduCAgG1FcAAhAC/gEQAQAA6A+oAqgC/g+oAqoK6gffVwAAEAL+ARABQACoAugKCAR+AogFaggMBuBXAAIQAv4BEAEAAvwCtAK2D7QCtAL8AgAC91cAAJQC1AKeD/QCEAhIBv4BCAEIAPgHAAz5VwABEAL+AQABJACkB7QEpgSkBLwErAckAPpXAABIAUgF/AR4BXgHeAV4BfwESAVIAQAA/FcAAhAB/gEQAQAAtAe0Aq4LNgg0CPQPNAAAWAAACAH/AAAG/wEFDnUERQT1B0UEdwcAAAJYAAA4CAoK7AqoCq4PqAqoCuwKCgo4CAAABVgABH4FVgV2BVYFXgcABUoFMgU6BUYEQAAGWBACEAH+AYAAMAD8D6oEqAT+B6gEqAQAABVYAAj+CgoKdgoACnwPPgo8CrwK/AoECAAAIVgQABAEfgUABV4FOgcaBXoFOgVeBVAEAAAkWBAEEAT+AxAKgAi+BqoEqg+qCqoKvgqACCpYAAAQBP4DEAMED/4JVAtUC1QJ/g0ECQAAL1gAAJgJnAl8BVwDDgFcAVwHfAlcCVgFAAAwWAACEAH+ARAA/A8EBPwGfAd8BfwFfARAADFYAABUAXQB3gd0AQAA/g9iCLIEMgPuDAAENFgAAggC/gEIAEAJ/gVqA2oFagNqCX4HAAA1WAACEAH+ARAB1ABUAN4HdAVQBVgF1gcQADpYAAEIAf4AgAm+BKoE6gM+ACoA6g++AQABQVgACEAJfgtqCyoKPg8qCqoKagt+C0AJAABKWAAEEAL+AxABAAF8CFYH1QD8B1QIVAt8C0xYCAEIAf8BCAFAAN8BVQXVB5UBVQnfDwAAUVgAAGgIzAp4CkwKKA+ACnwKVApUCvwIAABUWAACEAL+ARABRAAkByYFVAVUBSYFJAdEAFdY2ggCCooKqAosCqwOqgr6CiwKrAqoCAgAWFgAAggB/wEIAQAG/AEcBl0F+gVcBXwHFABaWAACEAL+AZAFBgWqAnoKigk6BsoAJgcAAF5YAACECrQKtAn0CrQKtg70CrQJtAqECgAAaVgAARAB/gEQBTgE9gfUB9QE1Af0BIQHAARrWAAEEAL+AxABAAj8C7wKvgK8CvwLBAoAAHVYAAAADP4H6gT+BWoFawd+BeoF6gX6BAAAflhECHwJfAv+C3wKAA+ICmgKHgr4CAAJwAiDWAACCAL/AYgICAj6BV4DWwFeD/oJCAQAAIVYAAh+C2oL/gtqC34PEAqSCvoKFgpyCAAAilgAAKwIrAruC7wKiA/oCjwKSAr4CAAJwAiTWAABRAX8BH4FfAV8BXwHfAV+BPwERAUAAZdYAAIQAv4BAAF8ANcP1Ar8CtQK1wr8DwAAmVgAABAD/gGQAAQA7AfkB/4G5AZsBeQHAACcWAAAfgSKBnYGAAZWBzwFtAX0BRYFZASAAJ5YAAAQA/8BgAA8AOYPdAV8BWQF5w88AAAAqFgABCAEfgU6BXoFPgd6BT4FOgV+BSAEAACpWBAE/gMQA3AC2grcD/gDAAr+BIgDeAwAAK5YAAD+CAIKfgoACvYPOwp+Cn4K3gp+CAgIs1gAAhAC/gEQAQQI1AfcB94D1APUB9QLEAjBWEAAfgRqBWoFagVuBzQFPAV2BTQFPAUABMdYAAAQAf4BAAF8BMwH/Af+B/wHzAd8BAQEylgAABAC/gEAA/QK1A70Cd4F9ALUBvQIAAjTWAAM/gPKAf4G/ga+B8IHEgf6BvIGtAQAAdVYAAAQAv4BEABEBbwGvAO+C7wHvAHkBgAE2FgABHAFfgV+BX4FfgcuBX4FfgV+BXAEAADeWAAAEAL+AQAAXAKsDjwJbgk8AuwG/AgAAOJYAAAQBPwDAAj8BwQA9A5kCAQO9AqkCgAA5FgAABAC/gEABXwE7A3+C8YB/AfsBfwJBAHrWCAAIAQgBCAEIAT+ByAEIAQgBCAEIAAAAO5YAAIYAgAB/g8AACAIIAj+DyAIIAggCAAA71iACJ4HkAD+DwAAIAggCCAI/g8gCCAIIAjwWAAABAzUA1QBVAHeAVQBVAFUAdQDBAAAAPJYAAjECFQIVAZUAV4AVABUB1QIVAjEBAAA81gACMQIVAhUBNQD3gDUANQHVAhUCMQIAAT2WAAAxAhUClQIVA9UCF4IVA9UCFQKxAgAAPlYAAA0BLwFfAV8BX4FfAV8B/wF3AUUBAAA+lgAAGQINAu0CvQOPgj0DrQKtAo0C2QIAAD9WJQA3Ab8BvwG/Ab+APwE/AL8CtwP1AKAAARZgAhgCN4ECAPoAhgEAAj+CyAIQAiACAAAB1lAAFAASA+sCrYKpA+0CqwKzArED0AAQAAJWQQKRAo0CYQFpAaeBMQG/AaECZQIJAgAAA1ZAAAICAgKdgneB1wFXAVcBVwLfAkECAAID1kACAQKfAl8BfwHfAV8BXwHfAl8CQQIAAgVWQAAQAAgCBAISAROBIoCCAGIAGgAGAAAABZZQABgCFAEjgKIAWgAEAAgAP4PgAAAAQACGlkAAFAISAlECTQFpgSUBkwCRAHAAEAAAAAcWYQARAD0DwQARAhmBLQDFAOUBHQEFAgAAB9ZIADwAywJ6AkICPgHAACoCJYEVAJMAcAAIFkICagEdgVUAswBEADMAyoB6AkICPgHAAAiWcQAfApsCm4L/AtsBWwFdgNsAXwAxAAAACVZAAj8BNQC/A/UAvwAEAhICdYGtASMA4AAJ1kQCBAEEAIQAdAAPgBQAJABEAIQBBAIAAApWUAIRAhEBEQCRAP8AEQBRAJEBEQIQAgAACpZEAgQBBAEEAPQAj4EUAiQARACEAQQCAAAK1lACEgESARIAkgB/gBIAUgCSARIBEAIAAAtWQAAQAhECEQERAREA/wARANEBEIEQghACC5ZAACACPgIiASIA/4AiAGIBogE+AiACAAAL1mAAEgIyAioBJgDzgCICIgImAgoB0gAiAAxWWAIcAROBEgCyAF+AMgBSAJIBEgEQAgAADRZQARICFIEVAREAsABfgBAAkACQARACAAAN1kAAAQI7ASsBKwC/gGsA6wErAq8CoQJAAA4WQAAKACoAKgCuAOsAqgKqAq4CqgGqAAoADlZAACACIgIqAiIBIgD/gCIA4gEqASICIAIOlkAAKgAqACoAJgGjACKCJgImA+oAKgAqAA+WQgJiAhoBAgECAP8AAgDiARoCIgICAkAAERZSAAoAOgHuAKsAuoHqAqoCrgK6AtICAAAR1kAADQAtAe0ArQCrAKuAywINAj0BzQAIABIWSgEqAyoApgIrAiqB6gAmAKoAqgMKAAAAElZAACgAKwCbAIsArwCrg+sAiwCbAKsAqAAS1lAAEQA5A9UBUwFxgdMBVQFZAXkD0QAQABOWRQEVARMBVwFVgV1B1QFXAVMBVQFFAQAAE9ZkABUCFwFfAVcA94BXANcBXwFXAlcCJAAUVkAAFQJVAl+CVQFQANCAyIFHglCCT4JAABUWSABJAkUBawDJAEmAXUBJAGMDxQBJAEgAVVZJAlUCUQJJAUcBQYDBgF8BQQFFAUkCQAIVlkgCSQJEAl+CQAFSANEAyYFJAkcCQQJAABXWSAAJAEUBfwHdAV2BXQFdAV8BRQNJAkgAFpZAAmECawFZAW0A6oBYgNSA0IFSgUACQAAYFkAAAQF/AXuBe4F3APcA+4F7gX8BQQFAABiWSgBKgGqAK4P6gq7CqoKugqmCqYPKgAqAGVZAAAACXwJDAVkBXYDJANMBQQFfAkACQAAZ1kACXwJBAVsBWwFLAN+ASwDLAOEBfwFAAloWQAALgkgCX4FAAUkA5IBFgORBf0FFQkQCWpZAABUATQB/AP0B/YB9AX0BfQD/AGUARQBblkAABoAmg/+Cv4K+w/6CvoK/gr6D0oAAABzWQAAEAgQCdAIMAkeBRICEAKQBXAEEAgQCHRZiAj4BA4DyAE4CAAMNATEA8QCPAQACAAAdlkICPgMDgOIA3AIBAf8AAQEPAgkCOAHAAB4WQAA+AgOBYgDeAwAAEQARAD8D0QARABAAHlZAAj4DA4DiAF4BgAA/AcQCP4JCAj4CAAGfVkACOgMHgOIA3gEAABECEQI9A9MAEQAQACCWQAACAj4CA4F6AMQAPwPBAQEBAQE/A8AAINZAAj4CA4FiAN4BAAAxAdECEQIRAj8CAAGhFmACIQIvAqkC6QKpgSkBKQHpAikCIQIAACGWQAAGAOAAP4PAACQCHAJHgYQA/AEEAgQCIdZCAj4BA4DiAF4AAAERAREBEQERAT8DwAAiFkACPgMDgPoAhgEcAFEAUQJZAlcDMADAACSWQAI+AwOA+gDGAzwA0gASgBKAEgA+AAAAJNZCAj4BI4CyAE4AgAIaASoBT4CqAVoBAgIllkABPwEBwPEAjwAAAwiAqIBfgChASECIASZWQAA6AieBsgBOAIACDwEgAT+AgACjAEwAJ5ZEAjwBA4DiAN4AAgI4A9cCEQI/A9ACAAApVkAAEQIVAbEB2QGbAJEAmIF8gRKBEAIAACoWQgA+AkOBYgDcAQAAAgP+ABOCEgIyAcIAK5ZAAj4DA8D+AgADP4DEgDSB5IIkgheCAAGs1kAANAIPgUQA/gEAADUAwoI+A8IANgBCAK5WQAI+AgOBYgDeAAADEgCSAH+D0gBSAJIBLtZFAlcCVwLXAvcBX4FXAVcB1wFfAkUCQAAxlkACPgJDAb4BQAA/AdEBFQFVAxEDPwHQATJWQAA6AgfB8gBOAbAASQAJAD+DyQA5AMAAMpZCAj4CA4H6AEQBngARA/EAP4PRADEBwAAy1kAAOgNHgOIAXgCAACwD64EoASsBDAHQADQWQAA+AkOBugDEAj8DyQJJAkkCfwPAAgAANFZAAD4DA4DiAN4BAAAkA+QBP4EkASQDwAA01kAAOgIHgXIAzgAIAicCJAI/g+QCJAIAAjUWagIrAisC5wKzAb8BIwCmgWqBKoIqAgAANpZAAj4BA4D8AgMCYAE/gMAAP4HQAicCIQG3FmgCKwIrQquC6wGvASsBq4FrQSsCKQIAADlWRAI8AQeA5ADeAQAAagHfgkoCTgJKAUkAOhZAAj4DI4DcAQEAOwMrAL+AawCrAS8CQAI61kAAPQMjwLkARgA/g+SBJIEngeSBPIEAATsWQAI+AQPA+gBEAD+BwIE8gSeB5IE8gQCBPtZAAD4DA8D6AEQAv4PAgXiBLoFAgT+DwAA/1kAAKIIlArUC8AGpgSUBIwGlAWkCKwIAAABWgAO+AEIBNgFeALYAwgI/gQIA8oEKggABgNaAAj4CA4H+AQAACQJJAm+DyQJJAkgCQAABFrICMgIrgqYC9gG/gSYBpwFqgTqCMgIAAAHWgAI+AQOA8gBOAZQADQPHAAUADIPUgCAABhaAAj4BA4D+AYAAPwPVARWBNQBVAO8BIAEG1oAAPANHgP4AAQIcAlcB+QBZAN8BcAJAAkcWgAA+AyOA3gIAgb+AZII/gf8DwIA/gMAAB9aCAj4CA4F6AMYAMAPvAK0ArQKtAr8DwAAJVoACPgMDgP4AgAKlAj8DxIE/gKQBVYIAAYpWgAI+AgOBcgDOAj4CJQElgP0D5wI8AgABDFaCAj4CAwFiANwCAwJVAfUAVQDVAVcCQAJNFoAAPgMDgP4APIPBACgAfoHogACCP4HAAA2WoAIwgj+CtoL/gaiBsgGtAWUBKwIpAgAAEZaSAhqCQoLYAscBUwFXAUuBSwHXAlECQAASVoAAPgMDgP4AAAITAw0A0YA9AcUCPQJAARaWgAA6ASeAsgBMAC+B2oFagVOBZoHKQAoAGZaAADoCJwGyAEwBoQDvAD8D7wAvASgAwAAdFpAAF4JQgkyC44FAAVeBUIHMglOCQABAAB2WggE+ASPA3gCAAD0A1QB9gdUAVQB9AEAAHdaAADwCI8HfAyABXwAbAluD2wBfADEAQAAf1oAAPAMDgP8BAgAog/aAN4CagtqCuoPIgCSWgAE+AQOA/gCAACEBr4B7AesAb4ChAQAAJpaAAj4CI4HeAAAB/4Ayg+uCqoKqgruDwAAm1rICL4EiANwCAQOXAH0BdwG0gbaBVIIAACzWgAA8AkcBvABAAz8AVQJVgNVAVQF/A0AAL1aAADwCI8FfAIADP4Aqg6qAP4EqgGCDwAAwVrICL4EiAN4AgAAVAW0AnYJtAfUAFQDQATCWgAA+AwOA/gCAAq8CKwH/gSgBqwFvAgAAMla6AgcBcoDMAKIDPwDBAiVBvYBlAaUCAAAzFrICT4FiAN4CAAIXAX4D1gB+A9cA/gFSAjhWgAA+AgOBegDGADgDxwC9gVUBRwC9A8AAONaAADwCI8FfAIACPoBogm+A6oLqgmiBwAA6VoAAPgMDgPwBCQC/g/eD2QBcAyOA3gEAAgJWwAA+AgOB/AEBAB8D9wLXgtcC9wLfA8EAQxbAAD4BI8DfAIAAJYP/gPWBdYH/QCVDyQAUFsAAEAARABEAEQIRAjkB1QATABMAEQAQABUW4AAhAiECPQPTABEAAAA/AcACAAIAAgABlVbQAFCATIBLgkiCaIHagEuAWgBSAE4AQAAV1sAAIwAhACUCJQI1ge0ALQAlACEAIwAAABYWwABiADIDzgADAEoCSgJqAdoASgBCAEAAFlbgACECOQHXABEAAAB8AgACP4PAABwAIABXFsAAEIIQgjyD0oAJggwBG8EiAPIAjgECAhdWwAAkABUAVQBVAl+CVQH1AFcAVQBEgEQAV9bAAAAABAEkAeSBJIHugSWB5IEkAcABAAAY1uQAlQCVAK0CpQKvAeUA7ICUgJSApACAABkW4AAhAj0B0wAAA/8AAQO/AUEBvwIAgMADGZbAAA4AQoBLAEoCSoJrAdoASgBDgE4AQAAaVuECIQI9A9MAAAIKAW4BGoCLAOIBEgIAABrW4QARAj0D0wAxASUAuwApA+UANIEBAUAAHVbAAD8C3QH8AD8D/wBCAAkCaoPYgEqAQAAeFsAAOACPAK8ArwKsAq2BqADvAI8AuACAAB9WwIA+gK+Ar8CugrgCrAOtwP2ArYCNgIAAIFbAABYAEgASAhICMoHSABIAEgASABYAAAAg1sAABgACADoB4gIigiOCEgIKAgICBgGAACFWwAADAEkASQBpADmB5QIlAiUCIQInAYAAIdbAACcAIQAlAiUCPYHlACUAJQAhACcAAAAiFsAACwApAAkAyQAJggkCPQPJAAkACwAAACJWwAALAgkBeQFNAUmAiQDpAJkBCQMLAQAAItbAAhcDEQERAPEAPYPxABEAUQCRARcCAAEjFsAAJwIhAiUBJQDlQCWAJQHlAiECJwIgASPWwAALAQkA+QINAjmB2QEJAQkBSQGJAgIAJdbAACcBIQClAKUCJYPlACUAJQChAKcBAAAmFsAABwABAD0B1QFVgVUBVQFdAUEBxwAAACZWwAABgDyD5IEkgT7B5IEkgSSBPIPBgAAAJpbAAgcDIQDNAI0BPYPtAi0CLQIhAgcCAAAm1uAAIwIRAQ0A6QBRgDkByQIJAkkCewEAACcWwAAHAQEBPQHVAVVBVYFVAX0BwQEHAQAAJ1bAAAMCLQItAi0CPcPtAi0CrQKtAocCAAAnlsAABgJSAkICSgFCgPsAQgDCAUIBRgJAACfWwAADAlcCVwFXAP+AVwDXAVcBVwJHAkAAKBbAAgsDCQC5AE0BCYC7AcsCaQIpAgsBAAAoVsAAAwA5ANUAVQB9gdUAVQBVAHkAwwAAACiW4ACLAEkAaQPtAlWCVQJtAmUD4QBDAGAAKNbAAAcCAQI1A9UDVYNVA1UDdQPBAgcCAAApFsAAAwIhAqUCvQK1g9UCnQKdArECowIAACmWwAADAD0D1QJVAlWCXQPVAlUCdQJHAgAAKpbAADMCMQItASkA6YA9gCkB6QIpAiMCAAGq1sAABwABAd0BVQFVgVUBVQFdAUEBxwAAACuWwAAHAAEB3QFVAXWBVQFVAV0BQQHHAAAALBbAACMAIQC1AKUApYPlALUApQChAKMAAAAs1sAABwBRA9UC1QL9gtUC1QLVAtEDxwBAAG0WwAADAkECXQLdA32BXQFdAd0CQQJDAkAALVbAAAMCOwHLACkArYCpAKkCjQJ5A8MAAAAtlsAAFwFRAVcA7wKXgmcB5wAXAFEAhwEAAS5WwAALAIkAZQPRAkmCSQJRAmUDyQBDAIAAL1bAAAMCNwLXAh8BF4DXAR8CFwI3AscCAAGvlsAAAwJBAX0BVQFTgFMAcwFTAVEBQwJAAC/W4AATADkBxQABADWB1QFdAVUBVQF1AcAAMJbAABMA0QI9A9UAEYLFAh0BJQD1AQ0CAAIxFsAAIwAxAfUBbQFtgW0B9QA1AjED4wAAADFWwAIDAjkC7QGtAb2A7QCtAa0BuQLDAgAAMZbAAAMAaQHhATkBE4HpASUBEQEFA9kAAAAx1tACFQE1ANUANQHVggECHQLVAvUCFQIAATMWwAADACEB/wF3AXeB9wF3AX8BYQHDAAAANJbAACMBLwCvAH8BL4IvAr8CLwJvAKMBAAA01sAAIQP9AD0AvQC9gP0AvQC9Ab0BIQHAADbWwAIDAjEC9QL9AfWA9QD9AfUC8QLDAgAAN1bAAAsAQQB/APECX4IfAt8BXwFfAvMCQAI3lsAAAwK5Ar0CvQG9gP0AvQG9ArkCgwKAADfWwAATAlkBbQB9AmGD5QBpAPUAzQFnAgAAOFbAACMAIQF/AX8A/wD/gH8BfwH/AGMAoAA4lsACWwHRAH0DwQA9gn0C/QF9Af0CKwJAADlWwABXAlcCbwKnAt+B0QGvAR8BXwBDAEAAOZbAAAUCNQH/Af8A/4D/AP8B/wH3AcUCAAA51sAAAwBxANUA9QLagvsD2QDRAPUAwwDAADoW4AEtAK0CrQL9AK2DrQC9Aq0C7QKhAIAAOlbAAFMAWQP9AtUC3YPVAtUC/QLVA9EAQAB61sAAAQB9AT0AvQA5gLEAfQA9AX0BIQDAADsWwAIDAjEC9QH1AfWA8QD1AfUC8QPDAgAAO5bAACMCEQI1AP0CtYO1AL0AtQLRAiMCAAA9VsAAFQI9AfUAfQK1AcGAPQHdAt0C3QOAAT2WwAEFgjWB94H1gPHA+4D7gP+B+4HLggAAPhbAAAIACgAaACIAQgICAgICP8HCAAIAAgA+VsACCQMRAKEAXwCAALQABAJEAj/BxAAAAD6W6AAqACoAKgGqAS8AKgIqAjoB6gAqACgAPtbAAAAAUQBVANUA1QBVAlUCVQHfAEAAQAB/FsAAAABPAFUA1QFVAFUCVQJVAdcAUABAAH+WwAIKAxIAo4BaAYIBFAA0AEQCP4PEAAAAP9bQAREBVQDVAH0BV4BVAlUCdQHVAFEAQAAAVwAAKQEpAS/B6QEpAQAANAJEAj+BxAAAAACXAAAAgF+AVYFVgF/CVYJVgnWB34BAgEAAARcgAT+BKoC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/g8QAP4GkAVWDAAA6lwAAPgDAAL+AwAC+AfAAKYPkgiwCMYPiADtXPgDAAL+AwAC+AMAAO4PoAK+AqAK7AcAAPBcAAD4B/8HAAT8BwAAJAVXBdoPVgVSBRAE9lwAAAAO/ARcBVwHXgVcBVwFXAFcCUAHAAD7XAAA+AP+A/gDAAAoBYwFygKIAi4FaAQAAANdAADwA/4D8AMAAFQDxAD+B8QAVAFEAgAAB10ACXwFGAFYCVgJXA9YAVgBWAUYBXwJAAAOXfgHAAL+AwAC+AMAALQDrAKuCzQI9AcgABRdQAAsAOgPWAVYBV4F+AdYBVgFWAVcBQAEFl0ADP4DDAisCqwKrgrsDqwKrAqsCq4KCAgXXQAAAAD2DxQAVAZ2BtYHdAZ0BhQI9gcAACldAAAMDOwDqAnoDw4ACAjoB6gCqArsBwAALV0IAWsBWgHKB0oBSwECDPoDSgDGD0cAAABKXQAA8AP8A/ADDADsBgwB/A8MAWwCqgQABEtd/AMAAv4DAALwBf4Dyg+qCq4KqgruDwAATF0gAOwPKAUoBegPDgDIDCgCqAEoBqwIYABQXQAA5g8cANwJXAn+D1wNXA2MCPwHBggABmldEACWD/QA9AD0B/QF9gX0BfQH9AiWDxAAi10AAAAA8Af+A/AJ/ARcDV4DXAtcCUAHAAC6XSAAkwCqAqoEsgcHAP4FrgWuAa4F/wUACM1dIAl8C3gFeAV4Cw4A+ATYA/gD3AbQBvAC3V0ACAAM/AMAAAAAAAD4BwAAAAAAAPwPAADeXQAAcAgABv4BIAAAAP4HAABwAAAA/g8AAOFdAAhECMgHAAT4CAQLcAiMCSAK2AgECwAI4l0AAAAF+wWoA6oD/Q+oAaoDrgX9BQAFAADjXQAAAAn+BagFqgP8D6gBqAOsBfoFAAkAAOVdAAQEBAQEBAQEBPwHBAQEBAQEBAQABAAA5l0AAAgMCAPICHwISghICMgPSAhICEgICAjnXQQCBAL8AwQBAAEEAHwERAhECEQMxAMAAOhdAAAAAP4PkgSSBJIEkgSSBJIE8gUCBAAA6V0EAgQC/AEECQAE/AMEAHQABAD8DwAIAAbrXQAFhAR0BIQEBAT8BwQFhAR0BIQEBAUAAO5dgAioBKoDrgioCfgJqA+uCaoJqAmACAAA8V0AAAAA5AckCCQIJAgkCCQIJAh8CAAHAAHyXQAABAD0B0QIRAhECEQIRAhECPwIAAYAAvRdAAAAAPwHRAhECHwIRAhECEQI/AgABgAA910AACABKAHoB74KqAqoCr4K6AuoCCAFAAH7XQAAIAGqAOwGuAquCrgKKAtuCKgEIAEAAP5dAAD4AwgACAAIAAgA/g8IAAgCCAL4AwAAAV4AAAQA5AMkACQA/A8kACQCJALiAwIAAAACXgAACADoAygAKAD+DygAKAIoAugDCAAAAANeAACIAYgA6AdcAEoA6A9IAEgESATIBwgABV4AAPwIAAb+AQAA+AMIAAgA/g8IAAgC+AEGXgAA+AEIAP4PCADwDf4DIgACAP4PAAgABwheAAD4CAAG/gEAAPQDFAAUAPwPFAAUAvQBDF4AACgBKgGqB+wAtACkD6wAqgSqByAAAAAQXgAA/AD/DwQA/AEACP8HIARoAKYDIQQgBBVeAAD4AwgA/g8IAPgD8A+IBI4EiAT4DwAAFl4AAPgDCAD+DwgA+AOAD0AEfgRIBMgPCAAYXgAADADEB1QATABOAOYPRABUBMQHDAAAABpeYACuB64BrgGuAa4PrgGuBa4FLgNgAAAAHF74AwgA/g8IAPgDAAh8BkQBRABEA3wMAAAdXgAA5ACkB7wApADmD6QAtASsBKQHZAAAACVeAAD4D6wEqgS4BwAA+AMIAP4PCAD4AwAAJl4AAGQApAeuAKQA7g/uD6QApAQuB2QABAArXgAA+A+uBKkEuAcCAPIDEgD+DxIA8gMAAC1eAAb8AQQAFAd0AVQB1gdUAXQFFAUUBxQALl4AANQA1AZ+AVQBVA8AAX4BAgVeA2IAAAAvXgAA5AAkBz4BNAH+DzQBNAU+BSQG5AAAADBeAAD4CAAG/AFAALwHvAD8D7wAPAdgAAAAM14AAPgA/g8IAPgBAAj+B1YE1gBWA1YFQAk2XgAA9AC0B64AvgD0D7QAvgSkBK4HVAAUADheAAAYAAoHfAFYAVgB3gdYAXoFCgcYAAAAPV4AAPgBCAD+DwgA8AGeD8IKygrCCt4PAABFXvgBCAD+DwgA+AEAD7wKrA+sCqwKvA8EAExeAAD8AQQA/w8EAfgF7wKVAfUAlQevBAACVF4AAPwA/w/8ARAErwS/Ba8CvwKvBb8EAARVXgACRAH8B34BfAH8B3wBfgX8BXwDRAEAAWFeAAD4AQgA/g/4AEAAPA+UCrwPsgpaD0AAYl74AQgA/g8IAPgBAAT0BvQG9gf8BvQGEARjXgAAegA4B34BOAG6DxABTgU0BVwGxAAAAGteKAA8DvwD/gP8A+gPwAPUA+QL3gsEDgAAcl5AAEQARABEAEQA/A9EAEQARABEAEAAAABzXoAAhAC0AIQAhAD8D4QAhAC0AIQAgAAAAHReAAAwAQgB5gElASQB/A8kASQBJAEkAQAAdl6ACIgIiQb6AYgAiACIAIgA/g+JAIgAgAB4XpAAlAK0AtQClAKeD5QC1AK0ApQCkAAAAHleAAD8AqwCrg+sAvwCEACsAOIPpACIADAAe14AADAGrAViBDAFEA4EAAQIBAgEDPwDAAB8XggEMAbOA0ECOAsABAgD/wAICAgI+AcAAH1eAAD8ByAFvAXwBe4G/gcQBOwFAAT8DwAAfl4AAK4IuQSkA4AKngjgBY4G+Aa0CKAMAAB/XgAAAAz4AwgACAAIAA4ACAAIAAgACAAIAIFeAAz8AwQAJAAkACQIJgjkByQAJAAkACQAg14AAAAO+AEIBAgGyAUsBAgEiAQIBQgGCAiEXgAAAAz8AwQIRAhFCPYPRAhECEQIBAgAAIZeAAz8AwQIRAhEBEUD5gBUAUQGRARECAAAh14ADPwDBAD0D0QIRQQGAPQHhAhECCQIJAaKXgAG/AEEBCQCJAGlAPYPZACkASQCJAYAAI9eAAz8AwQAlACUCNYI1Ae0ALQClAGEAAAAkF4ADPwDBAgEBPQDNAE2ASwBLAHkAQQAAACTXgAM/AMEAJQC9AKeAp4C1A+UApQClAIAAJReAA78AQQEdASEBRUE5gQEBoQFdAQEBAAAlV4ADPwDBAAECPQPlASWAPQMlAGUBpQIBAaXXgAM/AMEAAQPBAkECfYJJAkkCSQPJAAAAJleAAAADPwDBADkDyQJJgn0DyQJJAnkDwAAml4ABvwBBASsBKwErgL+AawBrAKsBPwEJAScXgAM/AOEAEQA9A8EAKYAJAkkCPQPJAAAAJ5eAAz8AwQIJAYkAfQEJgTsBywJpAikCAQGn14ADvwBBAQ0AiQJ5gW2BqQGpAW0BCQIAACmXgAG/AEEBJQElAT0BdYC1AL0BZQEFAQAAKdeAAz8AwQIRAk0CUQJ9g9ECTQJJAlECAAAq14ADPgDCADoA+gD6APsB+gD6APoAygCAACtXgAM/AMECFQHdAbUBQYIVAr0C1QKVAoAALVeAAz8AwQAVAD0B9YC1AfUCtQK9AtUCFQEtl4ADvwBBAQkByQA9A6mAKQO9AAkAiQMAAC3XgAG/gECBKoCqguqCPsHqgKqBvoFIgkAALheAA78AQQA9A/0A/QD9g/0A/QL9AukDwQAwV4ADPwDBAj8B6wGrAL+CwQA9AsECPQHAADDXgAM/AMECEwF/ANOAUQB3AdsCWQJFAUAAMleAAz8AwQIdAV0A/QPdgH0D3QD9AVUCAAAyl4ADPwDBAj0D7QItAb2DAQA/A8MAPwHAADTXgAM/AMEAPQLvAu8D/YDBAD8DwwE/AMAANZeAAT+A4IAlgiOBmYHvwZiBW4FRgKeAIIA314AB/wABAL0A7QDtAe2A/YHBAb8AawE/APgXgAM/AMMAOQPvAKmC+4PRAj0BSQD5AwAAOJeAAz8A0QALAusCn4ERANsBfQHtAkUAAAA414ABv4BAgjqBfoF+wX7AfoB+gPqA+oFIgTzXgAAgA98AAQLfA/+DyQEdA10CXQDdAUAAPZeBAi0BKwDZAQABfQJBAkECfwJJAkiCSAJ916ECDQHrAVECAAKJAokCvwLJAoiCiAKAAD6XgIIcgwuA+IECAWqCaoJ/wuqCaoJvgkICf9eAAAQABAA/g8QBBAEEAQQBP4PEAAQAAAAAF9ACEQIRAb8AUQARABEAEQA/A9EAEQAQAABX5AIkAiQBMwDiwCIAIgAigDqD4wAiACQAAJfAAAACR4FqgMqASoBKgEqAaoPLgEgAQABA18ECSQJJAW0AywBJgEkASwBvA8kAUQBBAEEX0AJVAlUBVQDVAF8AVQBVAFUD1QBVAFAAQpfAAl6CTgFPgN4AXoBEAEsAbYPNAFMAQQBD18ICEgESATIB0gESAQIAn4AiAMKBAoICAYTXwAAAADyAJIAkgCSAJIIkgiSCJ4HgAAAABVfAADEAKQIpAikCLwHAAAAAAAA/A8AAAAAF18AAIgI6AioBvwBqACoAPwPqACoBLgDAAAYXwAB5AikCKQIvAcAAAAH+AQEBIAEAAcACBtf8giSCB4HQAD8ByAIEAj/CRAICAn4CAAGH18AAAAEyASqAqwCqAH4D6gArASqBLgDAAAgXwAA8giSCJ4HAABACP4PQATQBEgBRgZACCVfBAHkCKQIvAigBxADzAgICPgPCADIAAgDJl8AAPIIkgieBwAASARoBtoFTAQoBwgMAAAnXwAA8giSCJ4HAA7+AQIO/gUCBv4IAQMADC9fAAACAqoDogKuAqMCogKuCqIK7goCBgAAMV8ABLQCVAJUCFwIwAcAAPQKVApUCNwHAAA1XwAB5ACkCKQIHAdAAP4HVgTWAFYDVgVACTdfAADkCKQIvAcAAMgJTgn4B0gFTAXYBRAIOV8AAPIIkgieBwAA8ANWA/APWANUA/ADAAI6XwAA9AiUCJQHHADICVQJ9AdUBVQH3AcACD5fAADkCKQIvAcAAPwCsAL2D7ACuAL0AgAASF8AAHkESQTPAwIA/QKvAvgPrwKtAv8CAAJMXwAA8giSCJ4HAAD6D9IC/g/SBBID+g8QAE5fKACeAtQDoAPsA/4D/AvgC/4LWA80AmAAUl8AAPwIAAT+AwAARAREBEQERAREBPwPAABTXwAAIgQsBSAFIAU+BSAFIAUoBeYPAAAAAFVfQARUBVQFVAJUCtQHVAFUAlQD/ATABEAEYl8gBCIO/gEiACIA/gciAAgERAQiAhEBkABkXwAAQAz+A1IAQgj+D0AACAjEBCICEAEAAGVfAAzkAyQIvAq8Cq4KbAk8BaQEpAIkAgAAZl8AAAAG8gEWCFoFUwWyBL4CEgISARABAABpX0wEXAJEAeoPwgBaAwAIiAREAiICEgEAAGpfAAjwBxAA3g/UAZQHNAgAClAKSAokCQAEa18AD/wAVAf8AtQKVAn8BwAAmAxEAiQBAABsX4ABSAD/DygAwAD/DygAiAxIBCYDkgAAAG1fBAj0BLQFvgS0BLQG9AQAAFgMSAImA5AAcF8QAvQC/AL0AvYP/AL0AhAIWAREAiIBoABxXyAE/AJ8CXwPfAF8BfwBAAjQBCgEJAOQAHdfiABIAOQPEgAADAgC+AFKCEgISAjIBwgAeV/IAEQA8g8JAEAI3gRCBUICQgb+BWAIGAh7XwAAyADkDwIAIAD8ARAMBAP8CAQI/AcAAHxfkABIAOQPAgT4C8gISAl+BkgG6Al4CAAAf1+QAEgA5A8CAOgIqAb+AagA/g+oBLgDAACAX4gATADiDxoAgAiICIkI+g+ICIgIiAgAAIFfiABIAOQPEgAACOQPBAj8D0QIRAgECAAAhF+QAEgA5g8QAIQIpAikCJQPrAikCKAIAACFX5AASADkDwIAqACoBqgAvAioCOgHqACgAIhfkABIAOYPEQAACP4PUgjSAVIGfgUACQAAil+AAEQA9A8AAPwPBAT0BRQF9AUEBPwPAACLX0gAJADyDwIAqAKsAqwC/g+sAqwCvAIIAoxfyAAkAPIPCQAgCyQJ+gaoBKQGsgUgCEAIkF+IAEgA5A8CAKAGkACoCOYPqACwAqAEAACRX4gARADzDwAAOglCCRIJ6g+CCT4JRgkAAJJfiABEAPMPCQAgDKQDJAT/DyQJJAkkCQAAk1+IAEgA5A8SAAAM1gMYBPAPmAiWCJAIAACXX5AASADmDxIAQAF+BWoBaglqCeoPfgFAAZhfiABIAOQPAgAoCSgF/gMAAP4PKAEoAQAAmV9QAEgA5A8CACAOvAMgBP4PqAioCKgIAACeX4gASADkDwIAIAycAwIE4A8cCRIJIAkAAKFfAABEAPIPGACsByYE/AcEAPgPBAD8AwAAqV8oACQA8gcKAAgFvgT+BbwCvAK8BbwEBASqXyAAJADzDwAA/gcKAOoPagV+BWoF6QcIAK5fQAAjAPgHBgDoB28A6AMkCXoExQM8DAAAs19IAEQA8g8AAPQOlAD0Dp4I9AmUCPQCAAy0X4AARgDwDwwAUAXeB1AFSAj0BIoHeAgAALVfAABOAOAPPACwBb4DMA08BHAEjgP4AggMt1+IAEQA8w8AAnoAagL6BO8FegRqAnoAAgO5XwAAZgDwDxQA/A+2AvQPIAj2BIgDeAwAAL1fAADHAPAPpgPoCe8HaAMmCXwGiAN4BAAIw18AAAAD4AAAAPAHBAgICBAIAAZgAIADAADFXwAAgAVgBAAC8geECEgIIAgQCAgG5AAAAcZfeAAAAP4PEAAEBoQJRAgkCBQIDAgEBwAAzF8AAAAPNABUB1QIVAhUC1QIVAhcA0AMAADNXwAIVA9UAEQGJAicCQQKRAhECDwDAAQAANdfCAQoBygAKAcoCL4IKAkoCCgGKAAIDwAA2F8EBAQDPAAkByQIpggkCSQIJAYkAAQHAADZXzgAAAD+DwgAAAAIAPgHCQQKBAgECAQIBNxfAAz8AwQAhAcEANQHFggUCBQIRAbEAAQH4F8AAAAMeAJIAEgO/AhIC0gISAh4AgAEAATnX3gAAAD+DxAAAAwQA/4AEADQDxYIEAYQAOtfOAAAAP4PAABQCEAESAP+AEgBSAJ4BEAI8V94AAAA/g8QADgICAQIA/4AyAcICDgIAAb1XwAACAwoAygAJAcqCCoJIghkBCgBCA4QBPtfeAAAAP4PEAAADPwDJAAkAOIPIgAiAAAA/V8ABFAHSAAmBxwIxAo0CowIhAREADwDAAT/XxAMiAKMAFIGMAgSCRIKkghkCAgDEAwACABgcAAAAP4PAACUAUQAJAD8DwQAZACEAQABAWCICIgOSABIBygIWAkOCTgKSAiIAogMAAAOYCAIMA4IAAYGBAj8CVQKVApUCFQDBAwAABJgAABEDFwDJgA0B0wIAAtMCHQGZABcDoAIFGBwAAAA/g8QAAQI5A8ECPwPRAhECEQIAAAVYHgAAAD+DxAAAAD4D4gEjgSKBIgE+A8AABZgAAAQAP4PCABAAOgDOAAuAOgPKAAoAugBHGA4AAAA/w8AABgASABEAnMCZA3IAFAAEAAdYAAAAA98AFQHVAh8CVQLVAhUBHwBAAYAACBgAAgIBugArAaqCKoKqAqqCqwI6AIIDAAAIWB4AAAA/g8QACAAsA+sBKIEqASQByAAAAAlYAAAEAiIBqwAqwSqCaoKrgioBPgAAA4ACCZgeAAAAP8PAACQAIIAkgD+D4IAsgCKAAAAJ2B4AAAA/w8IADAIjgiICP4PiAiICAgIAAAoYJAMiAJGAGQHHAgACXwKhAiUBJQAXAYAACpgeAAAAP4PCABCCSYJKgmSDyoJJglACQAAL2BAADAA/g8QAEAISAbIBX4ESAVIBkgIAAA7YAAEAAN4AEoHTAhICUgJTAlKCHgDAAQAAENgcAAAAPwPEACAAKgGqAC8CKgIqAeoAIgARmB4AAAA/g8YAAAL7AhUC1QIFA70CQQIAABLYAQAJAyUAkQAPAcGCYQK/AgECDQDRAwACE1geAAAAP8PCAAiCCwG4AE/AOAHKAgmCAAGUGAAAEQIJAc8AGQHYAgeCQoJAgg+A0AEIARSYCAAGAD/DwQAAAT9BSUFJQUlBSUF/QUBBFVghAiUBm4AJQc8CEQJAAl8CUQIRAN8BAAAYmBwAAAA/g8QAMgHKACeCAgG6AMIBMgJAABkYHgAAAD+DxAA+A8ICPgPDgj4DwgI+A8ACGVgAgL+A1ICUgL+DwIAwADyBwQIKAjABgAAaGB4AAAA/w8QAAAA/g9SCNIBUgZ+BQAJAABpYAAAAA78ANQG1Ai0CbwJtAqECPwCAAQAAGxgcAAAAP4PAAAwAIQPpAT8BKQEpASiByAAbWAAAKAApAJkCj4IpA8kAD4CZABkA6AEAAFvYAAIAA7+AKoGqgirCasKqgiqBP4AAAYAAHBgIAAwAP4PEABAADAPqASmBKQEqA8QACAAc2AAAAAM/gKqAKoGqghqCaoKqgieAEANAAF1YAAAAgz6AqoAqgz/CaoKqgiqBPoAAg4AAHZgQAhKB1oAQgd+CEIJQgp+CEIIWgNKBEAAfGAgADAA/g8QAMAHCARKBcwEKAUIBOgPAACEYDwAAAD+DwgAAADyB1QBUAFeAVAF9gcAAIVgeAAAAP4PEAAACPwEkgOQAJIP5AgICBAEiWAAAJAMlAJUALQG/AkUCjIIWglaA1AEAACNYHgAAAD+BwgAAAF+AWoB6gdqAWoBfgEAAJRgcAAAAPwPMACwAPADrALoA6gKqAboA4gCn2A4AAAA/wcIACgAqge6BK4EqgS6BKIHAACgYBAACA9+AAAHfAgYCV4JJAlUCEwDRAwAAKNgAAjgBLwCtAC0Bf4JtAq0CLQEvADgDgAIpmA4AAAA/g8IAAAIeAbOAUgAzA9KCHkIAAaoYBAICA78AAIGSAgmCoQK9AgECCQDTAwAAKlgOAAAAP4PAAAQAM4PEARGBeAECATmDwAAqmAECXQFVAFUAfwFVAlUC/wJVAlUAXQNBAmsYCAGoAC8APwAvAK8BLwFvAT8ArwAoAYgBK9geAAAAP4PCADyDwQAUAPaADILAgj+BwAAsmBACFQG1AJUAD4GAAkAC34JVARUAVQOQAi0YDgAAAD+DwgAQAE0AQQBhg80ASQBRAEAALZgAAD+DyoAKgM+BoAIPggqASoLKgj+BwAAuGB4AAAA/g8QAAgCrAKcCrwPnAKqAioCAAC8YBAA/w8IAAAA+AKoAqgCrg+sAqwC/AIAAsVgPAAAAP4PCAD8D7wCvAK+ArwKvAr8DwAAymBAADAA/g8QAAQE9AKUCJYPlACUAvQCBATLYHgAAAD/DxgAxAg0BKQDRgD0BxQI9AkABNFggAz0AtQA1Ab0CIQJhgp8CMUENgEEDcQA1WBAADAA/w8IAAAJvgTqA6oEqgOqCL4HAADYYDwAAAD+DwgA/gcCANYAcgFaBQIE/gMAANxgOAAAAP8HCAAQANQHXgVUBVQFXgXUBwAA32B4AAAA/g8IACAA/A+qBKgE/geoBKgEAATgYAAAhAa8ALwCvAT+BbwEvAT8BLwAhAYAAOFghAy8AqwA7ASECYQK7AqsCKwIvAKEBAAA5mAAADAA/g8IAIAPfAAEDwUJ9gkkCSQPBADnYDwAAAD+DwgAAAn+BVYFVgFWBf4FAAkAAOhgeAAAAP4PCABQCFgKvApaBRgFnAIoAlAA6WAgCCQO8gAKBoAI9AqECvwIlASUAJQOAADrYAgADAz8AVoBXwX2CVYNXglaBfoBCAwIAO1gAAAwAP4PEABkAl4C5A8AAPwHIgDiDyIA72B4AAAA/g8IAAAI3AlaBF4DWgjaCw4ICADwYDwAAAD+BwQAEADMB+YB7AH8BewF7AckAPFgOAAAAP4PCAAAAN4PwAZuBdAGRATaDwAA82AAAMgOKAD+BigIAAr8CVQJVAlUA/wHAAT2YHgAAAD+DwgAgAj8CtQK1g/UCtQK/AoAAPlghAiUBFQA9gVUCVQLVAtUCVYF1AEUDAAAAWEAAEwOLAD8BioIgglICmAIHgQgAEgOiAAIYRAACAzoAegF7AnqCAoKbAgMAeQFCAgIAAlheAAAAP4POADwD7gC9A8SANYDGAjIBxAAD2EACAgM+gFeAVoFWwlaC1oLXgn6AQoEAAAVYUAAMAD+DxAAhgCqA64KoAquCqoGjgAAABphAAjgBjwAvAa8CLwJ/Aq8CrwIPAHgDQAAG2EAAJQKVAocCtQLnAW0BRILWgkUCDAIAAAfYYAIfgYCAOoGqgiqCkIKHwnjBBsBCgXCACNheAAAAP4PEABMCFQE3ANUAVwJVAlcB0AAJGEgAHgAAAD+D4AJVARUBFYDVADUBRQIEAAnYQAAEAD+DwgAAAh8BlQB/gdWCFQLfAsABD9hgAd8AAQHXAA8ArwEfAU8BLwEvAIEAAADSGEEBLQC7QCmBpQIBAq0Cu4IpQSUAIQGAABLYQgI+AV3BHQAdgXwCe4KVAlUBRIBiAwACExhQAAwAP4PAAAUCHQHVgBUD1QAVg9UBBQATmF4AAAA/g8YAAAK/AdcB14DXAv8CwQKAABVYUABRAH8AHwBfgR8B3wBfgD8AnwBRAEAAVhhQAAwAP4PCABwC2wL2gokBVYFVAKUAgAAYmE8AAAA/wcEAPAE3gT+Bd4C/gLeBf4EAARjYXgAAAD+DwgAAAj+B/oG/gL6AvoG7gcICGdhAAAcBvwA/gL8BOAF/AT8BN4C/AAcBgAAaGF4AAAA/g8QAPwHVAJ8CSAGRAH8D0QIQAZuYQAO+AEIDNgB2AneCfwL/An8BewBKAwAAHBhwAg8BmwCPAD8DHwKAApoCAgF/AEIDAAIdmEADPwDbA3sCnwLbAduB3wH7AtsCvwIQAh3YYAAYAD8DzgI/AdEClUF/g9UBVQF9AlECH5hAABiDvoAqAZyCAIJGArGCDQERACcBgAAgmEAAGIKfgs+Cz4FvgW+BT4HPgm+CCIIAAiLYQAI+g44AP4GGAh6CRgKRwhUBDwAxAYAAI5hOAAAAP8PBAA8AOUPbgV8BWwF5w88AAAAkGFAADAA/g8IACAL6gaIAb4AiAPaD4oCAACRYYQEZAIIAIAAPAN8BzwGvAR8AbwC5AcAAJRheAAAAP4PCAAADPwBWgFYDf4BWAlYAQgNpGEgABAA/g8YAPAL3Ab0BvYC9ALcBvQHEAioYYgI+ga6AL4G+giACogKVghkCFwChAQAALJhAAAsCOQFfAH8B3oL/A18CXwF5AEsBAAItmEgABgA/gcIABAG9AD8B/YF9Ab8APQGEAS+YXgAAAD+DxgE8ABYBrgIyAo+CcgELgGABMJheAD8DxAAIAT0B/QH9gf0B/QH9gf0ByQEx2EAAJIIqgZuAFoF5Aj+CpoIughaAq4MIAnIYUAAMAD+DxAA+AP2B1wBsgZ+AeIHTgEAAMlhAA78ASQM9AIEAP4F/An8CPwC9Ab0CAAAymEgADAA/g8YAHwJDAVkA3YDJAVMBXwJAADQYXgAAAD+DxAA5ArUBvQF3gH0AtQG9AgAANJhAAAwAP4PCADwBv4PlAFwCPoPnAf0CQAI5mFAADAA/g8IAFIPegHKD14BWg9qAWoPAADyYQAAMwR8AwYAVAX3CNYKEAieCOQCvAQECfZheAAAAP4PEAD4AvwPmAJ8CNwDZAXsCwAI92F4AAAA/g8QAFwErA48Cm4DPASsBlwKAAD4YQAE/AZeAN4GXgjeCowKLAj0BSIAog4ACPxhIAAwAP4PkAE8B/wHnAfAB7wHvAe8BwAA/2EAAHQJ3A3eCVwNcAQkAqwMZglEAIQGDAAAYsgIHA6aAFACxAS+BWAECAUuBLgBVAcgAAhiEAAQCBAEEAQeAvACEgOSBFQIUAgIBgAAD2IAABQEJALEATwBBAIQCD4E0AOSBFQIEAYQYgAAAAz4AygAKALoCQ4EeAKKA+oECAgIBxFiEAESCZII/g+SAJIEkAQ+AtADkgRUCBAGEmIAAIgM6AOIAOgDiAgICP4ECAOKBWwICAYWYggE6AUoBSgFKAXoAQgI/gQIA4oEaggIBhhiAADAD0AEfgRIBMAPEAAQDP4CEAfWCBQGGmIADPgDCACICegPqAAICv4ICAfKCSoIAAYmYgAA9AKwAvYPsAL2AgIIEAT+AiAHlggQBipiAAAgAagPvAeoB6gHIAz8BSACJAWoCAAGMGIAAP4DWgP+D1QD6gMOCCAE/gIQB9YIEAYyYsAPOACYC/4N/AbsBcQHEAT+AhAH1AgoBjNiRgDqB3IFbgXiB34FfgUABH8CiAdqCAAGNGIIAvwK/Av+AvwL/AroAH4MiAPKBCgIAAY2YgAIAAb8AZQAlACUAJIAkgCSAPIBAAAAADdiAAgABvgBSABIAEoATABIAEgA+AAAAAAAOGIACAQM9AOUAJQAlACUAJQAlAD0AQQAAAA7YgAAAgL6CaoEqgSqA+oAqgOqBLoEgggAAD9iAAAADvwBFAhUBtYBVAFUCVQJVAdcAAAAQGIACPwHlACUAPIIAAz8AyQAJADiDyIAAABBYgAA4A8cANQPVAHWD1QB1A9UAVQJ3AcAAEdiAAAADvwBlABUCtYPFACUBFQCXArADwAAS2IAAIAAlACUCJQIlAj8B5IAkgCSAJIAgABNYgACCAYIAQgByAAoCBgI/gcIAAgACAAAAE5iAACIAIgI/g9IAAAA/gcACAAIAAgACAAGUWIAAIgAiAj+D0gASAAAAP4PEAAgAMAAgABSYogAiAj+D0gASAwAA/4AAAA+AMADAAwAAFNiiACICP4PSABIAAAABAgECPwHBAAEAAAAVGKIAIgI/g9IAEAMBAP8AAQAPAgkCOAHAABVYogIiAj+D0gASAgAB/gEBgRABIAHAAwAAFhiiACICP4PSAAAAEQARAD8B0IIIggiCCAGW2KIAIgI/g9IAEgAAAQEBAQE/AcEBAQEBARjYggBiAj+B4gAAAD8DwQEBAQEBAQE/AcAAGdigACICP8PSAAADEgD/wCIAAgA+AcACAAGaWKAAIgI/g9IAAAM+AMIAAoADAAIAAgAAABrYogAiAj+D4gASAAABEQERAREBEQE/A8AAGxiiACICP8HiAAACqIJcgQqA+YIIg7gAQAAbWKIAIgI/weIAEAIAghCDv4JQghCDv4JAAhuYggBiAj+D4gAIAhQBE4DwABCCE4I8AdgAG9imACYCP4PWAAACPgPAAgACP8PIAggCCAIcGKIAIgI/g+IAEAIEATQAz4AkAcWCBAIEAZxYogAiAj+D0gAAAf8AMQEBAU8A5AEcAgAAHNiiACIDP4DiAgADvwBZASkBSQCpAVkCAAIdmKAAIgI/g9IAAAISARIA/4ASANIBEgIAAB5YogAiAj+D4gAAAD+DyAEAAD+ByAIGAgQBnxiAAGIDPwDiAgADPwDBAD0BxQIFAn0CAAEfmKIAIgI/g9IAAAIEAQ+AtADEgXUCBQIEAZ/YgAAEAQQA/QABAqkCvQHrAIMAuQAIAMQBIBiiACICP4PSAAACGgEqAU+AigF6AQoCAAAhGKIAIgI/g9IAAAIeAgABP4EAAKMAVAAAACKYoAAiAj+B0gAAAD8B0QIRAh8CEQIRAj8CJFigACICP4HAAD8BwICAgD8DwQABAT8AwAAkmKIAIgI/w9IAAAAIggqCOoPNgCmAGIAAACTYoAAiAj+D0gAAA78AQQA/A8CAP4AAg8ACJViiACICP4HSAAACN4EQgVCAt4FUAgICAAAlmKIAIgI/g9IAEgAAAEiAUwBAAH+D4AAgACXYgAAiACICP4HSAAADOgDLgAoAOgHCAgABphiiACICP4HSAAADv4BIgAiACIA4g8iACIAmmKAAIgI/g9IAEAIBASkA3wA5AckCCQIIASbYgAAiAj+B0gAAA//APgHAAn4CA4K+AkABJxiiACICP4PSAAADIgDfgioBSgC6AUoCAgInmIICYgI/g+IAAAM/ANEAEQAxAFEAnwMAAigYogAiAj+D0gAAAD8DwQIFAvkCLQJBAoACKFigACICP4PSAAAANgHhAiCCEQICAgwBiAAomKIAIgI/g9IAEAAEADIBycIJQnICBAGEACkYogAiAj+B0gAAAz4A0gASgBKAEgA+AAAAKViiAiICP4PSAAAAP4PIgDyDDIDMgXuBAAIq2IIAYgM/gOICAAO+AHIBX4GSAXIBFgIAACsYoAAiAj+D0gAAACwD6wEogSgBKQEmA8gALFikACQCPwHUAAAALAHrgioCOgJCAn4CAAGtWKIAIgI/g9IAAAA/A8kBCIAPgziByIIIAa5YogAiAj+D4gAQAQIAkgB/g/IAEgDSAQAALxigACICP4HiAAAAPwBpACkAPwPpACkAPwBvWKIAIgI/gcIAOAPkASQBP4HkASQBPAPAADCYgAAkAj8B0AA6AyoAv4BqAD+D6gAuAcAAMRiiACICP4PiAAIAIAIiAiKCPoPiAiICAgIxWKIAIgI/g9IAAAI/AkkCSQJJAkkCfwJAAjGYogAiAj+B4gAAAz8AyIAogDiDyIBIgIAAMdigACICP4HSAAAAP4DIgKqCiIKIgb+AyACyWKAAIgI/g9IAAAEKATIBQoECAfoBAgEAADLYgAAiAj+B0gAAA/+AMgHAAn+CggK+AkABMxiCAGICP4HiAACACwBIAH+DyABLAEiAQAAzWIIAYgI/geIAAAA+A+IBI4EigSIBPgPAADQYogAiAj8B0gAAAi8BKQCpAGkCKQIvAcAANJiiABICP8PSAAAAP4PkgSSBJIEkgTyBAIE02KIAIgI/g9IAAgBgADkD1wERAREBMQPAADUYogAiAj+D0gAAAyIA34IqAUqAqoFaggICNZigACICP4HiAAgAJAAzgdICOgLSAjoCwgI12IAAIgI/g8AADgD5gIwCxAF8AMeCPAHAADYYoAAiAj+D0AAEADIAy4BKAHoCQgI+AcAANligACICP4HSAAAALwHIAT+ByAEIAS8DwAA2mIAARgJ/geYAAAImATUA5IAkADUD5gAoADbYoAAiAj/B0gAAACiD5oEhgSiBKIEngcAANxiAACUDJQC/AGSAAMAKAEqAf4PKgEqAQAB3WKIAIgI/gdIAEABFAFUAfwPVAFUAQQBAADfYoAAiAj+D0gAAAT+AwABvAwAAsABPgEADuBigABICP4HYAD+DIgDcAT8CQQI/AsACoAJ4WKIAIgI/g9IAAAO+AEIBIgHbgQIBQgFCA7iYoAAiAj/D4gAAA6QAX4MEATyB5QIVAgQBONigACQCPwHkAAABOgCuAqsCOgPiACIAggE5WKAAIgI/gdIAAAM/gOSAJIA/g+SAJII/gfmYoAAiAj+B4gAAASSBJYEkASYBJYEEAQAAOdiCAGICP4PiACAABgASAhICMoPSABYAAAA6GKAAIgI/geIAAACHAnQBH4FUALUBVQIEAjpYogAiAj+D0gAAAKkApwC1A+cAqQCJAIAAOxigABICP4PSAAAAJIPkgj+CJIIkgiRDxAA7WKAAIgI/g9IAAAEUATQBxAE/AEQBhQMAADvYoAAiAj/D0gAAAnSCCIK+gsmCNIIEgkAAPFigACICP4PSAAACIgE/gKIAIgA/gKIBIAI82KgAKgCagJsC3gL7gcoAygDbgKoAqAAAAD0YgABiAj+B4gAIAgYCSQJ4g8kCQgJMAgAAPdiAAEQCfwHkAAAACgBqAP8CqgKuAqoBiQA/GIAAQgJ/gfIAAAIiQT6A4gAiAD8D4sAgAD+YogAiAj+B0gAAACYD4QEogSiBKQEiAcwAP9iCAHIAfgB/AX6BfoD+gH8AfgB+AGIAQgBAWOQAJAI/AdQAAAAqAKoArwIqAjoB6gAIAACY4AAiAj+D0gAAAgkCSQJvg8kCSQJIAkAAAdjiABICP4PSAAAAN4HZAVkBWQFYgXiBxgACWMIAQgN/gMICEAIXAjEBXcGRAPEBFwIAAAOYwABEAn8B9AAAACoArgDrAqoCrgKqAagABFjgACICP4PSAAACYgE/gMAAP4HQAicCIQGFmOAAEgI/g9AAAQAVARUC0YJ1AhUCAQGAAAZY4gCSgIsArgKqgrsB6gCiAI+AkoCSAIAABpjAAAUAtQD/gOUC4AHtAOeA4QDPANAAyACH2MAAQgJ/gfIAAAIqAiIBv4BiAOoBIgIAAAgYwAAiAj+B0gAAAikBKYDvAC0D8wIrAiABCFjiAGICP4HSAAABC4FIAU+BSAFKAXmDwAAI2OAAIgI/g9IABAAWAlWCfQHXAFUAfABQAAkYwAAiAj+B0gAAAhEBEwDNgAkAFwPRABEACVjAACICP8HAACWAtICsgKaAtIPkgKWAgACKGOAAIgI/gdIAEAJLAUrA+gDKgMuBSgJAAgqYwAAiAz/AwAIkgb+AZII/gf8DwIA/gMAACtjgACICP4HSAAgCBwJYAn+DyAJHAkgCUAAL2OAAIgM/wOICAAO/gHCD1IE0gVSAVIOAAg6YwAAiAj/D0gAAgw+A+IEAAgiCf4JIgkiCT1jgACICP4HQAAQCPgIlASWAvQBnAeUCPAIQmNIAEgI/g8oACAAqge+BKoEqgS6BKIHIgBFY4AAiAj8B0gA4A+kArQC9A+sAqwK5AcAAEZjgACICP8HAAD+DwIEUgT6BVIEAgT+DwAASWOAAIgI/g9IAAAMvAMkBOQPpAikCLwIAAhMYwAAiAz+AwAIvAakAbwIAAf4AQAI/gcAAE1jiACICP4PSAAAAX4BagFqAeoPagF+AQABTmMIAQgJ/geIAAAA4g+sAqACvAKgCuwPAABPY4gASAj+D0gAAAS+BKoEqgeqBKoEvgQABFBjiACICP4HSAAAAO4PqgKqAqoKqgruDwAAVWMIAYgI/geIAAAA6A+oAqgC/g+oAqwK7AdcY4gASAz+AwAIvASsBawG/gSsBqwFvAgABF5jgACICP4PSAAACJQElgLUAZQIlgw0AwAAX2OAAIgI/gdIAAAI4AkuBCoCqgEqBC4I4AthY4AAiAj+D4gAIASYBSQEogUkBAgH0AQAAGJjiAz+A0AIEAj4BJQClgH0AZwClAT0BIAIY2MAAQgJ/geIAAAG/ASEBpYEhAa8CIAHAABnY4AASAj/B0gAAABqAr4Cqw+qAuoCqgIAAGhjiACICP4HSAAQAEwHUgV6BVIFRAVIB1AAbmNAAEgI/g9IAAAH/gAyB7IE8gSyBL4HAAByYwABEAn8D5AABALUB3AJXA1QDVQO1AhAA3ZjAACICP4HCABACfQJVAn8D1QJ8glQAQAAd2OAAIgM/wNICAIIqgaqBKoE/w+qCvoKIgh6YwABEAn8BxAAQAQoARgJTA9YASgBSA1AAHtjgACICP4PSABABwgAJAcqCCIJZAgoAwgEgGMAAIgI/gcAAPwPFAD0DwAAFA7qAQgGOAiCY8AARAb/AQAM/gMCAIIPggT7BJIEkg8CAINjgABECP8HBABgALUHtQD1D7UAvwTgAwAAiGOAAIgI/gfIAGAIaAikBKoHogS6BmoFAAiJY4gI/gdIAAAC+AKoAqgCrg+sAqwC/AIEAoxjAAAcAs0C/wL8Cv4H/AL8Av8CjQKcAgAAj2MAAQgJ/gcYAIwCqgLoA6gCqAqICPgHAACQY4gAiAj+D0gAAADQD0gFBgVEBFwFxA8AAJJjiACICP4PSABAAAgBKAHWDvwHKAEoAQAAlmMAAIgI/geIAAAA5A8UAMYIdAWkA+QEJAiYY4AAiAj+BwAA/g8KAOoOigjqD4oI7g4AAJljAAEICf4HiAAAAqwKpArqB6ICqgLqA4AAm2OAAJAI/AcAACgFvgcoBQAA/A9AAMAAAACgY4AAiAj+D4gAAAT0ApQIlgeUAJQC9AIEBKFjgACICP4HSAAABJQChAHUD4IBsgKKBAAAomMAAQgJ/gfAAAwEpAKUAcQPlAGkAqwEAAClY4AAiAj+B0gAAAikCqwLpgSkBrQFpAigAKdjAAGICP4PSAAMCKQIlAiGD5QIpAgMCAAAqGNIAEgI/g9AADAA/A+qBKgE/geoBKgEAASpY4AAkAj8B1AAAADoA7gCrALqB6gKuAroC6pjiABICP4PSAAAANQHXgVUBVQFXgXUBwAAsGMAAJQI/AeyADgG5gH8AxAIkgj+B5IAgACyYwgBCAn+BwgAAAG+A+oFqgSqAqoIvgcAALdjAACICP4HSAACDvgBTgcAAP4PAgD+AwAAuGMAARgJ/geYAAAC8ANSA1QD8A9cA1ID8AO6YwAAiAj+B0gAQABYCrwKWgkYBTwEWAKQAMljgACICPwHSAAACXQFHANcD3wDFAU0CRAAzWOAAEgI/g8oAAAIXAl8BdwDXgF8BVwFXAjPY0AARAT/AyQAAAD0D5QElwT0B5QElwT0D9BjAAEICf4HiAAADL4CqgSqD6oKqgq+CoAI0mMAAEQE/wNEAAAA6g+qBAoE/gcJBOkPAADWY4gAiAj+B0gAAATsB2wFbAVsBWwF7A8gAtpjBAGEDP8HxAAABt8DVQlVBVUDVQlfBwAA22OAAIgI/geIAAgIeAk2BZQDPANQBXAJAAjhYwAAkA78AQAM/AM0CLQK9Aq0D7QKvAoACONjAACICP4HQAAcD7AAsA/+ALAPsAC8DwAA6WOAAIgI/g9IAAAA/gdoBUAFfgVoBeQHFADqY0AAiAj+B0AAEgH+D0IAGgwAA/4AAAM4DO1jAAEICf4HiAAAAP4G6gSqBaoIqgi+BwAA7mMgAEgI/gdIAAAC+gK6AroC+g+6AroC+gL0Y4AAiAj+D4gAAgRWAvIJ1gbSBNkHVQhACPpjiACICP4HSADAAIwOpASsBOwHogSyBKoO/WMAAIgM/wNACB4IwAteCEAHVgjUCwQIAAQAZIAAiAj+B0AAEAC4BHYENAV8CbQKsACAAAFkAABICP4HAADyD0IAWAeqAtoCAgv+BwAAAmRAAIgI/g9IAAAIqgqYC94EmAaqBaoIgAAFZAAAiAj+B0AAGgj8BTgEugc4CO4JCggABg1kiACICP4PSAAACOwF7APsAewB7AXsCQAID2SAAFAI/AeQAAAAeAN4A3gBfgl4CXwHeAETZIAAiAz+A0gIAAzVA1YIfAlUD1cJVAkAABRkgACICP4HSAAAAKYJWglSCdYPWg1WD9IPFmQAAAgJ/geIAAACVArECqYKng+MCoQKAAAcZIgI/gdIAAAIvAi0CaAG/gSgBrQJvAgACB5kgACECP8HJACAD1oAWgdbBVoHWgDCDwAAKmTAAEgI/gdIAAAH/AB0BnUF9gV0BXQHFAAsZAAAEA78AYAM+AN+CfgHAADeDUICngUgCC1kAACICP4HSABAAG4HVgVcBWQFTgdEAAQANmRAAEgI/A8gAJAPaAB4B3QFcgV0BWgHEAA6ZAABBA3/B4QADAj/BasDqgH/BasDqg6CAERkAACIDPwDKAiABLwGvAG8BbwGvAakCQAARmSAAIgI/gdAABwFVA1cC9QJXAVUBVwJAABHZIAAiAj+B0gAAAasBKQE6geiBKoEqg4AAEpkAABICP4POALIATgBMAD+D6gE/gesBAAEVGQAAIgI/geAAIgCBAK0Au4OxAIEArQCgABYZIAAiAj+BwgA4A8UAFwH9gVcBxQI9AcAAGdkAABICP4PSABMAOgHaAVuBegHaAVsBQAAaWQADPwDBAC0A7QLtguEB7QDtAN0A3QDVAJ4ZIAASAj+D0gAAAl0CXYH9AF0A3YFdAkECXlkAAFEA3wD/AN+C/wL/A9+AvwCfANEAwABg2QAAvwD/AP+C/wLvAfYA9YDUgN2A1QCgAKHZAAAiAf+APAPXgD8B3AA9Ac6BsUBPAYABJBkkACQCPwHkAAYCPwL2AbeBtgC/g+YAgACkWSAAJAI/AcAAJgD/AvYC9wH2AP8AxgDAACSZIgI/gdIABAA9A++ArQK/g8QCO4GiAN4DJVkAACQCPwHAAD8DVgB/AUAAPgPJADkDyQAnmRICP4HWAAACPQK9Ar0CvYP9Ar0CvQKEAikZEAARAz/BwAA8g+/AtoPEAg4BMcDfAwAAKVkAAAICfwHCACgC7wK5A5ACygF/AeoCQAAqWSAAIQE/wNEAAAE+gFWCVMPVgH6ATIMAACrZAABkAj8B1AAAA38ASoB6A0oAegNKAHoDaxkAACICP4HSAACA9YPHgM2AK4DrQcoCwAKrWSAAIgI/gdYANQAVAe8BbwHlAW6BdIHkACuZIAAhA7/AWAE4Ae/BrUG9Q91BHUD/wVgBLBkAACICP4HoABOCfYFVgVAAU4F9gVWBUAJsmRAAEgI/g9IAAIJeAVuA+gBbgN4BWoJAAi1ZAAAiAj+B4gAAARcBd4FQA9cBU4FXAUAALxkAABIB/4AAA78ASwB3AesA38CpAMVBAADv2QAAIgI/gcIAOgMtAL0BBIA8gy0AuQECAjBZEAASAj+BwAAtAxkAwQA+gdUBfwHVAUABMJkAACICP4HCACcB8wFnAW8B5wFzAWYBwAAxWQAAIgI/gdAAHwEzAf8B/4H/AfMB3wEAATHZAAASAj+ByAAvgL6Ar4Cug++AvoCvgIAAMpkQAT8BPwH/gf8DvgPkAfuBqIG5gaoBAgBy2SAAEgI/gdIAAAAmgf8BdgF3gfYBf4FigfNZAAAkAz8AxAIwAW8BfQDlA/0A7wF4AkAAM5kFAA8Ar4DuAPOC7wLhA+oA7YDlAMsA0QC0mQAAYgI/geIAAgOxAGSB9oF8gO0CcQPAADUZIAAiAj+D0gAGA74Aa4MvA/MD7wPyA8AANpkgABICP4PAAT4AxgA2AX+A3wPbAFsBgAE4GQAAIgI/gcIAGAM1APkAr4CvAK8AuwPBADmZAAACA3+AwgJLAmUAdQJvg+0AfQBHA0AAOxkAACICPwHAABeDdQDVAwAAywE9A88CWQI9GSAAIgO/gEADPwDRAj0C/QL9gP0C9QLAAD6ZAAAiAz+A8AIdgfKDO4HCgBuBqoKrgoAAP5kAAGICP4HCADCCH4K3gdeB14H/gvCCgAIAGVUAMwCXgJMA+AL5gvUB8IDSANeAswCVAAPZQAAmAz+AwAI6AdmAvQPAADeB9QL9AoAABJliAj+B0gADAjeC14IXAZMAV4EXgisCyAAFGUAAIgI/gcAAP4H9gR+A/AH7gP2CP4HAAAYZQAAiAj+B0gE+APsD/wJxgH8B+wF/AkAABxlQAAkBP8DIACLD34B+gJ7BvoBegT7ByAAHWUAAJAI/AdQAMQD/AL8DzwA/AP8A+QHIAIkZQAAAABICP4P/gv0B/4O9A+uBPoHqAQAAC9lCAgoBGgEqAQoAz4CKAOoBGgEKAQICAAANmUAAPwDAAEAAf4PAAAwBG4EiAPoAhgECAg5ZQAA5AEkAiQBfAVgDDAEzgIIAegCGAQICDtlBAEEAfwBBAlkCRAEbgKIA4gCeAQICAAAPmUACAgG+AEqCMgHIAAQDO4CiAN4BAgIAAA/ZQQE9AMEAvwDJAJkCDAE7gIIA+gEGAgACEVlCADID0gEfgTID2AIPATKAogDeAQICAAASGUICKgEigKMAygEQAAwDM4CiAN4BAgIAABMZQAAkg+SBP4EkgSCDyAAFAzqAogDeAQICE9lQAD4AVYB9AVUBfQDYAkeBOUCxAM8BAQIUWUIAmgJCAn+D8gACglgCH4EiQPoAhgMAABWZQAEVALUAf4E1ATUA0AAfgaIAWgCGAYABFdlAAj8BVQFVAH8BWAAOAzMAogDeAQICAAAWGVQAlgJVAjyB0QAZAswCO4ECAPoBBgIAAhZZQACVAJUCd4P9AFYCTYIfASLA8gCOAwICFtlAADQBRgE1AQWA4gIMARuAogBeAIIDAAAXWUAAPYP0AH+D9AB9g8ACD4EyQPoBBgIAABeZQAA7g/gAX4CoAnuD0AIfgSIA+gEGAgAAGJlAAAIAvoDqgKqAv4PAAAwDO4CiAN4BAgIY2UAAOoPrwKqAq8KygcgABoMdQLEAzwEBAhmZQgC+ArYCtwO2AN4CmAI/gQIA/gECAgAAGxlRAD0A1YB1AkWDPQDMAhuBogB6AIYDAAIcGWgCKoKmAf+BJgHigggCD4EyQOIAngECAhyZQAA9A/0A/YCdAn0BwAA4Aw+AygD6AwACHRlQABcCDwIvg68CNwP2Aq2CrQKTAhECAAAdWUAAPoPDgP7Ag4J+gcQAD8MxAN8BgQIAAB3ZQAEvwKrAf8FqwW/A5gIPwTCA3ICDgwCBHhlCAl8C3wL/gV8BXwLYAkwBO4DeAQICAAAg2UAAF0JfgX/B1QDXgFoAOYDVgVcBWQFQASHZQAACAgICBgEaASIAgoBiAJoBBgECAgICIllIgQSDNIDVgFaAUsBSgFWAVYB0gcSABIAi2VkAGQPZAFUAVwP1gFUAVwPZAFkCWQHYACMZQgIaASOA3wCAAjUD/QHEAD+AxAEFg4AAI5lJAgkD6QAHAJUCVYPVAEsBSQApA8kAAAAkGWkCKwIrASsBZ4GwAKAAr4GrAWsCKwIoAiRZQAAJAT8AwAIKAbKATgCAAhECPwPRAgACJdlAAIQAiQBJAFIAQgBAAH+DwABgACAAAAAmWUMAKAD/g+gAKwAAAIkAUgBAAH+DwABAAGcZQAEWANUCPIPVAAEAyAAbAEAAf4PgAAAAJ9lQADIA3wF+AT4BXwFAAQoAIAA/weAAAAApGUAAAAO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rAL8BAQEAAB+Z4gBSAD/B0gAgAQ4BIYHYAQBBI4FMAYAAH9nCAOIAP4PiAAADPwDZAikBSQGpAVkCAAAgWeIA0gA/g9IAAAMhAN8CIQFNAIsBeAIAAiEZ4gBSAD+D0gAAACcAWoBCAnICQgM+AMAAIlniANIAP4PSACIAAAIRAhECPwPRAhECEQIkGeIA0gA/w9IAIgAAAz8AyIAIgDiDyIAIACVZ4gDSAD+D2gAgAg4BIgDfgDoBwgIOAgABpdnAACIAUgA/w8oACgCgAFIAP8PaACIAwABmmeIA0gA/w9IAKAIEARuBIgDyAI4BAgIAACcZwAAgAS+BKoCqgGqAf4PqgGqAr4EgAQAAJ1niAFIAP8PSACICCAEqAQoBT4CKAXoBAgInmeAAUgA/wdIAAAH/gAABIADfgCAAwAEAACgZ4gDSAD+D0gAAAEkARwBBgGEDzwBIAEQAaJniANIAP4PSAAAAPwPBAgUC+QItAkECgAIo2cAAAQBtACUBFQEfglUClQKlAq0AAQBAACqZ4gDSAD/D0AAEADIByQIIwkkCcgIEAYQAKtngAFIAP4PKABAD/wABAb0AQQC/AcADAAAr2eIAUgA/g9IAEAAEA+QBP4EkASQBJAHEAC2ZwAApAiUBI4ChAHcD4ABvAKkAqQEvAQABLdngAFIAP4PSAAADv4BCAzwA/wPBAT8DwAAxGeAAUgA/g8oAAAA9A8UAJQBfACUCRQI9AfPZwgBiAD+D0gAAAD4D4gEjgSKBIgE+A8AANBnAACEBIQEvgKsAewHrACsAb4ChASEBAAA0WcIAYgA/g9IAAAA/g+IBIgEiAT+DwgAAADSZwQJZQkRBRIDBAPEDx8DJAMkBSQJFAkAANNniASqBKoCgAKkAbQPjgGEAvwCwATABLAE1GcACVAJVAU0A3wD3A90AxwDFAU0CRAJAADYZ4gBSAD+DygAKAGAAPQPTAREBEQExA8EANxnCAOIAP4PSAAAAPwPJAkkCSQJJAnkCQQI4GcIA4gA/g9IAAAAWABICEoIzA9IAFgAAADlZwAASAgoCKgPWA1cDUgNWA2oDygISAgAAOxnAAAECPQEtAKUAf4PlAG0ApQE9AQECAAA72eIA0gA/g9IAAAA9AMUAfQJBAj8BwQAAADxZwAAiANIAP8PyAAICQAIigj6D4wIiAgICPNngANIAP4PAAD8CQIF+gIAAPwPBAD8AwAA9GegBL4EoAK/AqQBgA+fAaQCpAKkBKIEkAT7ZwAAJAgUCNQPrAquCqQKrArUDxQIJAgAAP9niAFIAP4PSAAAAOgDKAAqAPwPKAAoAugBA2iIAUgA/w9IAAAP/AAUDvQBUghSCNIHAAAEaAAEUARcAlAB0gDUD9AAUAFYAlQCUAQABAVoAAOIAP4PAAT+A0II/gdAAP4HQgD+D0AAB2iIAUgA/g9IAIgEIAKkCSQI5A8kAKQBIAYIaIgBSAD+DygAQAgQBFAEfgTIAyoFqgigBgtokANQAP8PUAAEBnwBRwjkD0QARAMEBAAAD2iIAUgA/g9IAEAEEgSWBJAEmASWBBAEAAARaAADiAD+D0gAAAbkARwDQADQCRAI/gcQABNoiANIAP4PQAAQCAgJJAniDyQJCAkwCAAAFmiIAUgA/g9oAPoPigV+BAoEfgSKBPoPAAAXaAQJdAlUBVQFfAPUD1QBfANUBVQFdAkECSFoiAFIAP8PSAAICGAEGATYAg4ByAIoBEgIKmiIA0gA/g9oAIAEEAROA8gA/g/IAUgCQAQ3aAgBiAD+D0gAAAFKAUoB+A9IAU4BCAEAADhoiAFIAP8PSACICCAEaAWYBE4CKAOoBIgIOWiIA0gA/w9IAAAA/g9SCNIIUgFSBn4FAAk8aMQBJAD/DwQATADkD2cEWwRaBGYE4gdAAD1oEARUAlQB3g9UAVQJEAT+AhAD0gQUCAAGPmhACGQJFAUEBTwDBg9EATwDBAUUBSQJAAhCaIgDSAD+D0gAAAgkCSQJvg8kCSQJIAkAAENogANIAP4PQAAMCYAE/gMAAP4HQAicCAQFRWiIA0gA/g8IACAO+AGWB1QIXArQCRAIAARGaIgDSAD+DygAQAD8DwQIVAr0C1QKVAoACEhoAACUCZQFtAVUA1YPVAF0A5QFlAUUCQAITGgAAAAJeAVYBVgD3g9cAVwDXAV8BQQJAABQaAgDiAD+DwAA/A8EANQDVALUAwQI/AcAAFFokAmYBV4FfgMWAYYPVgFaA1oFsAWACQAAU2jEASQA/w8kAAAE/QUlBSUFJQUlBf0FAQBcaIgDSAD/D0gAyghCCMQFcQZWAkAF3ARCCGNoiANIAP8PSAACBCwFIAU/BSAFKAXmDwAAZWiIAUgA/g8oAIAIVAQ0AxwAFAAyD1IAgABmaIgBSAD+D0gAAAF8AQIBkA8+AUgBRgEkAGhooASUBJACvgKAAagPpgGWApQCjASEBAAAaWjEASQA/wcUAAAO/AEEBCUE9QckBCQEAAB2aIABSAD+BwgA4g9SAVoB+g9WAVIJ8gcAAH9oiAFIAP4HSAAAAX4BagFqAeoHagF+AQABgWhMCWAJEgVYA0IDMg8OAUIDQgU+BQwJMAmFaBABkAD8D5AAoATYA6wCqALoC6gK6AeAAoZoAAOIAP4PSAAACSgN/gMAAPwPBAD8AwAAk2gIA4gA/g9IAAAAVAFEAcYPdAFEAUQBAACXaIgDSAD+DwAA/AisBqwG/AWsCKwI/AgAAJ1oAAAgAPwPAgD4AwAAqAauAJQPtACsAqQEomiEAUQA/g8kAAQA8QdWAVABXgFQBfYHAACmaAAAEgmKBF8ERgVqAkgCSgHfAEYACgAQAKdoiAFIAP4HKAAgAKoHvgSqBKoEugSiBwAAqGhACCoJGgV+BRkDqg8AAR4DQAVABX8JAAmtaIADSAD+D0gAQApYCR4FygaYBp4JqAgACK9oiAFIAP4PSAAABOoErAL4D6gArgS4AwAAsGgAA4gA/w9IAIAM6AOIAOgP/gQIB+oIIASzaIABSAD+D0gAAAy0AywApw8kALQPIAQAALVoAAA0CAwE3gNMAVQBQAJMAN4HDAgUCCQGtmiIA0gA/w9IAAAP/gAKAqoC6gdKCU4JAATAaAgBiAD+D0AAEAUIBSQEogQMBpAFUAQAAMRoRAlUBdQFzAPMAe4PzAHMA8wDzAVUBVQJyWiEA0QA/w8kAEAAngeqAKoA6w+qAL4EgAPLaIgBSAD+D2gAQAkIBf4FWAFYAf4BCA0AAM1okANQAP4PUAAAAPwPVAlUANQHfAkABQAA0miIAUgA/g9IAAAAbAI8Aq4PrAI8AmwCoADVaIQBRAD/D2QATAZEAVQE1wdUAEQBTAIAANhoAAD0DBQC/g8UAfQIYAQUAv4PFAL0DAAA2miAA0gA/g8ABP4Dkgj+BwAM/gOSCP4HAADfaIgBSAD/D0gAAAT8BKwCrAH+D6wBrAL8BOBoAAgYCQoFfAVYA94PWANYA34FCgUYCQAA42gIA4gA/g9IAIAIFAZUAP4PVABUBXwFEAjuaAAApASUAtQPjAKMBD4EjALUD5QBpAKABPFogANIAP4PSABACjQJ1AbeBJQGtAkUCAAA9WgAAMQBJAD/DyQAAAPfAPUH/wfVAF8DQAL6aIgBSAD+B0AAHAD8B/QHVgVUBWQFDAcAAAVpiANIAP4PCAAgALQHrAKuCywI5AcgAAAADWnIACgA/wcIAOAPBAT8BVwFXgVcBfwFBAQOaYgBSAD+DwAAMAD8D6oEqAT+B6gEqAQAABJpgAFIAP4PCAAgDf4DKAiACRwE5AN0BgwIHGmQA1AA/g8QAPAJOAk0BfIDNAM4BfgJEAgtaQAAkAD/DwAA/g8SAO4BAAD4D68C6A8AADBpgANIAP4PAAD8A1QC/A8AAP4PAgD+AwAAP2mAAVAA/AcAAKgA+A94BXgFfAX4D3gAgABKaRADkAD8D5AAAAj8B9QB1AXUA9QJ/AcAAFNpgAFIAP4DAAz+A0IAqgT6B6oEQgT+AwAMVGmAAUgA/g8AACwJfgkMB6ADHgVCCT4JAABaaQAAVAhMBl4DVATAD1QJTAleCUwI1AgACF5pAAOIAP4PQAAcCFQE3ANUAVwJVAlcB0AAY2nAASQA/w8UAIADfwDlD1UFVwVVBfUPBwBtaQgJagVsBXgDbgHoD2gBfgNsBWoFCAkAAHVpiANIAP8PCADiCaII7gj6C0IIkglyCgAAd2mAAUgA/g9IAAAAvA+oCoAKvgqkCqQPFAB8aYABSAD+DygAiAiqCpgH3gSYBqoFqggAAH1pAACkBKQEgAL8AdYH1AH8AYACrASiBAAAgmkAA4gA/g8AAPwHVAJ8CSAExAN8D0QIRASEaQADiAD/D0AAHgjACV4EQANOBtQLBAgABIZpgAFIAP4PCADwB7gC9A8WANYDBAjoBwgAlGkAA4gA/w8AAPwHVgJ8BwAA/A8EAPwDAACVaYgDSAD+DygADAGUD0QJJglECZQPLAMAAJtpgAFIAP4PKACACOwFvAOuD6wDbAUkBQAAnGmIAUgA/g8IAEAIXAbcAVYJVAlUCVQHAACoacQBJAD/DyAAggBKACYA9w+iAqoCqgIAAK5pAATiBOwErgKiAKAPsgCoAqYCqASiBAAAtGkAA4gA/g/IAAAAvA+iCpAKpA+cCoQKvA/LaQAAyAMoAP8PAAD8D74CvAL8A74K/AcQAs1pyAAoAP8HKAAIBugBfAd7BXwFdAV0BwAA0GmAAUgA/g8IAHwAVAhWB/wA1AdUCXwLAAjYaQgDiAD+D4gAAARWBVQA/A9UAlYFVAUAAP1piANIAP4PSAB6ANoP/wraCv8K2gr6DwAAAmrABOwE2gLAArwA1gfWAPwCiAL2BOAEAAAKaiQIFAl/CRQFUAVKAzYBQQM0BX8JFAkkCRFqiAdIAP8PSAGEASYJDA24Dw4BfgUQCQAAE2qAAUgA/g9oAAAIfAt8C/wFfgV8B3wJfAkZaogDSAD/D0gAHAVsAXwJbA98AWwFfAUABR5qAAOQAP4PAAD8BwQJ9ArUC1QJtAqECwAAH2qAAVAA/g9QAAAC9AL8AvYP9AL0AvQCEAIhaoABSAD+D2gAAAl0BXYD9AF0A3YFdAUECSNqhAFEAP8PRAAADSsDqgg+B2oCKwWiDAAAKWo4A7gA/Ae4ACgAgADsB7gHqAeoB6gHAAAqaogBSAD/D0gACAj6CV8FWgX6AV8FWgX6CTFqiANIAP4PAABeCToLjg0cBUIFegtCCV4JOWoAA4gA/w/IAOAItAq+DPQGAASQAP4PEABEagAAyAD+DwgA+gOqAqoC/g94BI4DeAwACEtqkANQAP4PEAAyD7IA8gf+BfIH0QCwDyAAWGqIAUgA/g8oAIAPWgFaB0oF3gZaAMoPAABZasABKAD+ByAA2gS6BboEtQS+B9oFEAQAAF9qiANIAP8PQADmDLgDtArfCKQHuAq0CKAEYWqAAUgA/g9IAAgEuAVsA+4KvAmsB6wBOAJraogBSAD/D0gAEgjyBX8FegX6AX8FcgXyCXFqgANIAP8PAAT8A0QErAdsBwQApAj0ByAAgGoAA4gA/w/IAHgIzAv8C/4L/AvMC/wLAACQaogBSAD/DygAiA98ACcH7gX2BeQF9AcEAJRqiANIAP8PQAAaB/wF2AXeB9gF/gWIBwAAomqAAUgA/g8IANgMuAL0BBYAxAyoAugEAAipagAByAH+B2gAeAXMAfwF/gfMAfwFhAUAAKxqAAOQAP4PAAAUBtQG9gN0C3YHVANUBAAAw2qAAUgA/gcAAP4HAgT+Bf4F/gX+Bf4FEgT7aoADSAD+DwgAfgkuC/4NAAVeBS4LXgkAAARrgAFIAP4PAAD+B+4E5gPwD+YD7gz+BwAACmsIA4gA/g+IATAB1A82D4APNg9UD3QJAAAga0AIMAgMBAoCiAF4AIgBCAJIBDgICAgAACFrAAAEBoQBCAhgCB4ECgPoAAgDSAQ4BAAIImsUBCQCxAE8AwQIMAQOA+gACAMoBBgIAAAjawAE/AMUABQA8gcCADAMDgPoAAgBOAYICCdrAAD8DwQFpATUBAQNYAweB/gACAN4DAgIMmuIAMQPogSQBJIE5AewDA4D+AAIAzgEAAg6awAACAn8BVgB/AUAATAMDgPoAAgDOAwIAD1rUAhYC1QI8gdUDQAIMAQOA+gACAM4BAgIPmsABKoCqgivB6oAogIwCA4G6AEIAzgMCAhHawAB/geqBKoFqgi+BwAAHA7KAQgCOAwAAElrJAStAv4H/AOuAf0EOAwHA+QADAMEBAAATGuAALwHrAW8B4QIvAewCA4G+AEIA3gMAABQawAA/gOCBNoFqgTaBQAMEAPuAAgDKAwYAFNrIAHsD6wHuAeoB+AMYAQcA/ABEALwDAAAYWsAAboPuweAB7sHmgcwAA8O5AEEA3wMAABiawAEAATwBwAEAAQABP4HIAQgBCAEIAQAAGNrAAQEBOQHBAQEBAQE/AdEBEQERAREBAAEZGsACPAPAAgACP4HIAQAAP4HIAggCBAIEAZlayAIIAu8CKAIIAggBP4HJAIkAqQBpAAgAGZrAAAQCNQHFATUB5QEEAT+ABAHFAgUCBAGZ2sACPAHAAT+ByACCApoCKgFPgIoBegECAhpa6AAoAV8CGAIIAUgBf4EJAKkASQApAAgAWprJAikCKQOlAiUCLwIhA+UCpQKpAqkCgAIb2sQANAHHATQBZAF3gWeBbQFFAT0B/AHEAByawgM6AMoAGwL6AdoAi4JfASsA7wEqAgoBnNrEA7QAVAE3ALQD9AAXgn0DFQDdAVUCRAEdGsADPwDBAhUCHQPFAiED1QK9Ao0CtQIAAB3awAM/AMECGwI/A5sCAQPbAr8CmwKpAgAAHhrAAh8B1QE1gc8BUAAPAe8APwPvACoBwAAeWsAAIQARAgkBFQEnAIUAZQAdAAUAAQAAAB7awAAxAhkBJwGFAH0AAQA/AdECCQINAgkBnxrwAAiCJ4EEgPyAAAARABEAP4PQgBCAEAAg2sAAMIIPgSSA3IAAAj4CIgG/gGIBvgIgAiJa8AAIgieBJIDcgAQAM4HqQLoCwgI+AcAAIprgABiCB4GkgFyBBAETgPIAP4PSAFIAkAEi2uAAOIIngSSA3IAAAioBP8EaANlBVUJQASWa0AAYgxeApIBcADkDwQE/AVuBWwF/AUEBJhrgABiCF4GkgFyCAIJqAUvBxgFXQsVC6AItGsAAPwPFAXkBBQNQAzeCEIHQgb+CWAIGAi1awAAAAL8D1ICUgIRCUAI3gVCAl4FoAgYCLdrAAz+A1UJVQk9B0AA4AheB0IG/gVgCAAAumsAAFIGVAHMD1MBAAjeBEIFQgJ+BeAEGAi8awAAbAxsA+4A7AcEAmAI3gRCA14DwAQACL9rAAf/APUOpQD1AqcEAASvAqECLwHoAgAEwGsAAHwJUglAB1QFPA1ACH4JQgb+CWAIAADBawAA/ATSBMAH1AQ8AkAI/gVCAv4FYAgAAMVrAABUATQL1goUB8QCEAjOBUIC3gVQCAAAy2sAACAA4Ak8BSQDpAF8CSQJJAf8ASABAADNawAAIADwAy4CogKqAqoKIgoiDv4DIAIAAM5rAABIAMQHcwJKAsoDegpKCkoO+gNCAkAAz2vAAMgC6AOmApQDlAPUA5QKlAr0B8QCwADSayABIgHqB2oFagX/B2oFag1qDeoHIgUAAdNrEAD4B7YFlAz0BwAAtA8sAKYPJAC0ByAI1GsAAAAA/g8gBCAEAAD+B0AIIAgQCBAHAADVawAAQAF+AUgBKAGADz4BSAFIAUQBJAEAANlrAABACn4L5AdkBVQDXgDkB2QKZAlQCUAE22sAACgBKAEoASQB/AckCaQIkgiSCJAIgAbhayQBJAH8ByQJoggQCMALfgpICkgKyAkIBOtrAADEBPwF7AXsA+4H7AvsCuwKfArECgAA72skASQB/AeSCIIIKApkChAJzgkQCswKAAQPbAAAAAj8ByQEIgQiBH4AogMiBCEIIgggBhFsAAAACPwHpASkBKQE5ASkAaQGvAiACAAGE2wIAPgBCgIIAgAI/g+SCJIE8gGSBp4IAAQUbCAAGABGAFUAVABUAFQAVADUAxQEBAgABBdsAAhYCUYFVAJUAtQF1ARUANQDFAwECAAEG2wQABgJhAUjAyoJKgmqB6oAKgHqAwoMAAYibAgAqAS2Bb4FfAd8BbwFvAUcAPwDBAwABiNsEAAYCUQFIwGqDyoBKgFqDSoA6gcCCAAGJ2wQAMgCxALeAtwP3ALcAtwCHAD8BwQIAAYobAgAUAlYC1YLXAVcB1wJXAkcAPwHBAgABi5sEAiICrYEXANcAlwEXAccCPwBDAYECAAG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AAQRcBAAJA8kAIAFvAT8DrwMvA78ArwEvAmAAh9wEQSmAwAAAgATDLoDOgP4DzoBuwe6A7oPLHAQDKIDhAA2BlQB/gcmAfIEbgVqAfoFAARMcAAAIghEBgQDdA7UDzYPhA92D1QPdA8AAFFwAAAiBqwBAATqA+oH+gXiAeoD+gXqBQAEY3AQACYPQAAXAk0DoAOcA7YL+gtcBxICQABrcAAIYAgYBAACgAF+AIABAAJABDgIAAgAAG1wAAACCEIEMgQCAsIBPgDCAAIDYgISBAAEb3BACHAEAAL+ARACBAAECAQI/AcEAAQAAABwcAQChAF0AA4I5AQkAgQB9AAEAWQCJAwAAHVwAAgACaoIKgQqAqoBKgIqBCoFvggACAAAdnBwBAAO/gEAARgFAAQgBCAE/gcgBCAEIAR4cAAAQAhICigJJAQWAtQBHAIkBKAJIAhACHxweAgABv4BEAEABjAAjgCICAgJCAz4AwAAfXAACAwJ0gQABAgD1gACAwgEDAXSCAAIAAB+cAAAmAiICGgECAIKAewACAMIBMgEGAgAAH9wOAgABv4BAAMIAPAHAAQABP4HAAQABPgPiXBwCAAG/gEAAjgIAgzyA5IAkgCSAPIBAACKcHAAAAz+AwACGAgwBA4C6AEIAygEGAgAAI5wAABACCgJpAQgBBgDxgEYAiAEpAUkCEAIknBAADAO/gEAARgKQAg4BAAE/gIAApgBIACVcDgIAAb+ARACAAjoByoAKgDoBwgICAYAAKtwOAgABv4BAAMIAEgEaAbaBUgEKAcIBAAArHBwCAAE/AMgAgAA/A8kCSQJJAkkCeQJBAitcAAM7AMoCKgJqAQuBqgBKAIoBCgJrAggCK5wcAgABPwDMAYAAPgHLgkoCegLCAr4CQAEs3AYCAAG/gEAAhgA4A8UAJwBfACUCRQI9Ae4cHAIAA7+AQACKAQwAA4A+A8oASgBKAEIALlwAAAADOACoACgBr4ApAKkBKQA5AIEBAAAunAAAYgMigLKALgGrACqAqgIuArgCIAHAAC8cDgIAAb+AQgBEAR0A14I9A9EAEQDRAQAAMFweAgABv4BCAMQADwHpAAkCPQPJACiAyAEwnB4CAAG/gEAARgBAASWBJAEmASWBBAEAADIcCAJEgcOA8oAagwaAAAEfAkAAQAF/gUACM9wAAgADPwBVAFUBVQBVgVUAVQLXAlABwAA2HA4CAAG/gEQAoAIiAT+AogAiAD+AogMAAjZcCAIEA7+AQADWABID64EtASkBLQETA9AANtwQAgwBv4BEAYABPgIiAj+B4gE+AYACAAA33B4CAAO/gEQAP4PAgQiBfoEogUCBP4PAADkcPAIAAz8AwAEEAUAAagD/AqoCrgKqAYkAOZwIAQYBAAD/wAIAwAI+QQJAu8BCQT5BQAI53B4CAAG/gEAApAIhASmA7wAtA/ECKQIgATrcAAIighiChQJAAQqA1oELgSaCIoKeAgAAO1wAAAkBCQDfwAUBoAAZAA/BwQAfAKABkAI+XAAACIIIgY+ADYFNgG3AHYCdgQ+ACICAgQJcQAAEAgUBPQBVAlcA1wBXANcCVwJFAcAAApxPAQADv4BCAMAAF4BVgHWD1YBVgFeAQAAFXEgCBAO/gEQAoAI+ASWAvQBnAL0BIAIAAAZcTgEAAb/AQgDIACsB6QEpgSkBLwEpAcgABpxAAAkChQLvgQMBKQDMAIMBH4FDAUUCCAEIXEoCCgN/gElAfwNJAH8BSQJ/AEkBSAIAAAmcRAIEAz4AV4BWA1YAfwBWg1YAVgBCA0ACDBxeAgABv4BAAI4ANAPSAUGBUQEXAXEDwAANnGICKgGbgJuABQFDAGAAGgGHgBoAooOgAg8cUAIOAQAAv4BEAikBPQDpgCkD/QIpAgABExxeAgABv4BAAKQCPwK1ArWD9QK1Ar8CAAATnEECfwEXQReAVwN/AAEAHYMBQH8AQQMAABVcQAAAAj+BLIAzgK6DAAA/gQSCRIDng8ACFlx8AgADPwDIAj0CpQK/AqUDvwKlAr0CgAAXnEAABgJVgVUAVwN9AEAABwN6gjIAjgFCAlkcTgEAAb+AQABEASEBL4CrAHsB74BhAIABGdxAAj8DZQAlADsDBQAzAFEBVQJVAHMDQAIaXE4CAAG/AEQAgAI/AusCqwCrAasBvwLAAhucQAAMA40APQBdAV+AXQFdAV4AfQBEgwAAH1xAAAQDv4BEA78A6wEbADqB6oEagL6DwAAhHE8CAAG/wEIAgAM/gCqBqsIqgqqAP4GAASKcQQEfAc/ALwAvgb8AAACZwSqAIkCRAwAAI9xAAAICvoK2gLaCv4D2gr6CtkC+QoICgAAlHEYACAM/gMQBCQBlA9ECSYJRAmUDyQBAACZcQAE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t3MEAiQC/AMkAgQAgA+ABP4EiASIBIgPCAC7cwAEJAL8AyQAAAz4A8gJfgZIBsgJGAgAAMJzAAQkBPwDJAIAAPQDFAH0CQQI/AcEAAAAynMAACQE/AMEAkAI/gdCAP4H/A9CAP4PQADNcwQCJAL8AyQBEAFICCQFkgTEBEgCEAEAAOBzBAQkAvwDJAIAAFwGSAH+D0gBSAJABAAA7XMAAEQE/AdEBAAC8AgABP4DAAj8D0QIBAjycwAAJAT8AyACDACMAvQClALUD5QClAIAAv5zJAQkAvwDJAoACPwFVANUAVQPVAn8CQAEA3QAAiQC/AMkAQwDaAIICP4HiAAKA2oEAAQFdAQCJAL8ASQBAAD8D1QEVgTUAVQDvASABAZ0AAQkAvwDJAIACXwIVAnUD/wPVAlUCXwJCXQABCQC/AMkAgAItAcsAKYPJAC0ByQIAAAQdAAEJAL8AyQCAAj0CRQEEALeARAEFAj0CyJ0AAQkAvwDJAKAANQC1Am8CMQHZACUAwAEJnQAAiQC/AEkAQAAtAe0AqwDLgj0DzQAAAAqdAAEJAL8AyQCAAgIBf4FWAFYAf4FCAUACDN0AAAkAvwBIAOIAP4PSAAAA8gA/g9IAIgDNHQAAFoAWgFeAXoBWgFYAXoFXgNaAVoAkAA8dAAIRAT8B0QGAADoBqgArA+oAKgB6AMIBEt0AAAkBPwDJAIAAPQHvAW2BbQPvAXwBQAFW3QAAiQC/AEgAQQE9gSUApAB8ACUApYC9ARcdAAAJAT8AwQI8Ae4AvQPFgDWAwQIyAcQAF50AAQkAvwDJAIAADwAsA+wB/4AsA+wALwPX3QAAJQOVAh8BFQGlApACVQJvARUAVQGAABqdAQCJAL8ASQBAAz+AKoGqgD+BqoBgg8AAHB0AAQkAvwDJAIACHwEVALUAf4HVghUC3wLg3QAAiQE/AMkAgQAdA9kA9YFZANECXQPAACHdAAAJAT8AwAM+gMqCMAHFgcqBOgPKAloCYt0AAAkAvwBJAAAAvQC9AL2D/QC9AL0AhACnnQAAiQC/AMgAQoEaAV+A+gBbgVoBW4JAACndIAAfAh0C7QLtAv8CwAPVAt2D1QLVAkAALB0AAIkAvwBJAUABP4Evge6Ab4CugJ+BRAFynQABCQE/AcgAggM/A/+B+wH7AfoC+gLAAjcdAAIAAb8AQQABAb8BQIEggc+CMIDAAwAAOJ0AAB0DfwB/A90AQAO/AEEBvwFBAL6DwAA43QEAE0N5gMADfwH/AsCCP4HWAHnD1QBAADkdAAE/AX+D/QJ7AHwDfwDBA78BQQG+gsADOZ0AAAEBAQO5AkcCNQEFAUUAPQHBAgECAQG7nQAAFgISA7kB3IHaAdiB2oBcg9kCEgISATvdAAA/AcEBbQE5AQQAQIM/guSAPIPAggABvZ0AACIDPoDiAD+D4oAAAj8D5QA9A8ECAQE93QAAEIIVAzAC2gJZAtWAUwBVA9kCGAEAAAEdQAAfAl8BVwHbAU8AIAPfAiUAPQPBAgABhh1AAAIAAgA/g+IBIgEiASIBP4PCAAIAAAAGnUAAAQBBA/+CVQJVAtUCVQL/gsECQQJAAAcdQAAkg+SBP4EkgcAAP4PiASIBIgE/g8IAB91AAAgCJAIjgiICIgI/g+ICIgIiAiICAAIInUADOQDJAm0CrwKrgqsD7wKtAqkCiQIAAAjdQAM5AMkCLwKpAqkCqYPtAqsCqQKIAggACV1AAArAiQC/wEkAQAIvgbqAb4EqgS+AwAAKHUAAAAM/AMkASQBJAH8DyQBJAkkCfwHAAApdQAAAAz+A5IAkgD+B5IIkgiSCpIK/gkABCt1AAAIAOgPqAKoAqgC/A+oAqwK7AcIAAAAMHUAAPwPRAREBEQE/AdEBEQERAREBPwPAAAxdQAA+A+IBIgEiAT+B4gEiASIBIgE+A8AADJ1AAAAAPwBlACUAJQA/A+UAJQAlAD8AQAAM3UAAPwDJAEkASQB/w8kASQBJAEkAfwDAAA1dQAAAAD8AyQBJAH/ByQJJAkkCfwJAAgABjd1AAAACXwJVAVUA/wBVAlUCVQJfAkABwAAOHVAACAA+AeuAqgC6AOoAugLCAgIDPgDAAA6dQAA/gciAv4DIgL+AwAACAgICPgHCAAIADt1AAD0DwQE9AVUBVQF/AVUBVQF9AUEBPQPRXUAAPgB/g+uD3gAIgnyBDoC5gkiDOADAABMdQAAAAG+CKoEagI+ACoAag+qAL4AAAEAAE91AACACPwP1AjUCPwI1APUBNQG/AiACAAAUXV4CAAG/AEABhAA/A9EBEQE/AdEBEQE/A9UdQAA/AckAvwDJAL8A4AAqAD+D6AArACAAFl1AAA8AKQPogqSCqIKgA+8CoQKRA88AAAAXHUAAEQA1A/UCvwK1g/UCsQKpAqkDyQAAABidQAAoACuAu4CrgLuB64CrgLuAq4CgAAAAGV1AAD8B/wDJAL8A5AAiA/WCKQI3AjED0AAZnUAAPwH/AMkAvwDAAgoCSgJvg8oCSgJAAhqdZAAlADcB7QFlAW8B5QFsgXaBdoHkABAAGt1SAB8BPwH/Af8B/4H/Af8B/wH/AdIBAAAcHUAAAAKvArsB6wGvAKsAqwG7Ae8CgAKAAB0dQAA/AckAvwHdAasA/QDXg1UDVQJVAcAAXZ1AAA4AAoP7AuoC64LqA+oC+wLCg84AAAAeHX8ByQC/AMkAvwDIAC0A7QCrgs0CPQHIAB/dUAA5gdZBdQHWAX/D+AEdgN5B7QIUghABoZ1AAB0BVQPXAzABwQA/A/0DvwP9A78D0QIinUAAPAJ/gj+D/4P/g+uD/4P/g/+CPAJAACOdQAE5AcEBPQDTAIECPQElAL+D5QC9AwAAI91AATEA/QDTAIACKQHPACmDywAPA9kCEQEkXUACW4JVAXUA1IBCA2iAyoE6g82CWIJIAiXdTgJgAT8AwQAJAAkCCcIpA9kAGQAJAAAAJl1MAkABfgDCABIBCgKrAmoCagIqAgoBAAAmnUYCYAM/AMECMQINAQmAqQBZAIEBAQIAACfdZgIgAb8AQQAhAD0B5UElgSUBJQElASEAKR1mASAAvwBBAD0A5QE9gSUBJQE9AQEAgAAq3WICLAO/AEECEQItAWWBpQG9AXECEQIAACudYwIQAb+AWIAIgDyBysIKwkqCfIIEgYiAK91OAGADPwDBAz0AxQAlgLUAxQA9AcECAQEsnWYAIAO/AEEDvQBVATWBXQCVAWUBDQIAAC5dbgIgAb8AUQAJAqUClYJFAWkBCQCRAEAALx1GAmADPwDBAAkBbQE1gjUCrQKlAAEAQAAvnWYCKAO/AEECIQEtASmAuQBpAKkBKQIAATFdZgIgAb8AQQA1A9UAFYD9ABUA1QI1A8AAMd1mASABvwBBATUBxQEFgT0B5QElASUBAAAynWYBIAG/AEEBCQFFAVWBdQHVAUUBSQFJATSdYgIkAb8AQQApAK0AqYC5A+kArQCpAIEANV1mAiABvwBBAD8D6wErgSsAawGfAUECQAA2HU4CYAE/AMECNQJVAtWCVQJVA3UCRQIAADbdZgIgA78AQQA7A+8Ar4C7A+8AqwK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JALkA3wA1AF+AVQB1Ad8AQABl3iEAPQHLATgB8QANAykA0YA9AcUCPQJAASYeEIA+gcWAvIBAAT8DKQC/wCkAP8CpAT8AJ94hAD0D0wEwAcIAHwNSAP8D2gDfAVICQAAp3gAAMoE6gL+D+oKgAr8CtQK1grUDvwAAACpeAAAhAD8ByQC5AsACPwHrAKsAqwG/A8ACLB4hAD8DyQE4AeIAA4J/A8ICPwPCgjICQAAsXgEAdQPLATEC/AHCACoDwgHfgKIBWoIAAazeIQA/AckAuQDAAisB2gALg2oAygErAkgALR4hAD0DywE5AcAAKgL2ArcCtgKqAsoCAAAuniEAPQPTATEB0wA5A9cCVYJ9A9UCVQJTAm8eAAAggD+ByICwAd+A6oEqgD+BqoBgg8AAL54wgD6ByYC4gEABP4DigjqB6oB6gKuBAAAwXiEAfwPJATkAwwIaA+OCOgOSAioD84IKA7FeEQA9AdMBMQHAAB0CFwG3AF2CVQJVAcAAMp4AABSAtIHegVuBWoHagLqB2oFegVCB0AA0HhQBVAFPANUDxYLfAsIC2YLUgu+D5gBgAHVeAAARAD0DywE4APID/wPzgjMD+wIyA8ACNp4hAH0DywEJATEB/wC3AbcDv4K3A/8AgQD6HgADvwBBAS0BLQOtAuWCoQKtAq0CrQOlAD0eEQA9A8sBMQHIACaCdoP1gjWDJoKIAgAAPd4QgD6BxYC8gOACHYHzAFfAMwB9QdUAQAA+ngAAIIA/gcSAuADSgjvBOoC+gDvAuoCAAwBeQAAQgD6BxYC4gEIDP8ArAb9AKwGLAAABw55hAD8ByQCxAsUCEwEXgfQD0wJXglUCRAIGXkAAAQB/APkCw4IVAfUAUAIDAfsDywJZAA6eQAAIAQkAqQBJAgkCOQPJAAkAKQBJAIgBDx5AACIAYoA7g9YAIgBAAD+BwAIAAgACAAGPnkAAAgBiADOD7gACAkgCCAI/g8gCCAIIAhBeQAAiABKAOwPWACIAAAA/A8EAHQCjAEAAEJ5AAGIAMoPeACIACAA/AcgCP4JEAj4CAAGSHkIAYgAzg94AIgJAAT8AyQAIgDiDyIAIABJeQgBiADuD1gAiAgACPAPAAj+DyAIIAgACFB5iACIAO4PWAAIAYAA+A9OBEgESATIDwAAVXmIAIgAzg98AAgI4AwAAvIHxAgwCGwGgABWeQgBiADKD7gAAAj8DyQJJAkkCSQJ/A8ACF15iAFIAO4PWACACDwEJAPkACQA5A88CAAGXnmIAYgA7g9cAIAA+AMoASgB/g8oAfgBAABfeQAAgASWBLQCtAC+B7QAtAK0ApYEgAQAAGB5gABJAOsHWACCAOoDKgEqAeoJAgj+BwAAZXkIAYgAzg94AAgAQAFKAUoB+A9KAUoBCAFoeQQJdAXUBdQB/AnUCdQH/AHUBdQFdA0ECW15EAAQAZgIbgRMAXQJSAlGD1gBZANUA4wMd3kIAYgAyg+4AAAEVALUAX4DVAlUD0QBAAB4eQgBigDqD5gAAADcD1QB9ABUC1QI3A8AAHp5CAGIAOoPWABACAgJ/gVYAVgB/gUICQAAgHkAAAQK/AuMA/wL3AveD/wDjAf8BwQKAACBeQAAFAlMBV4BTAlECVAPTAFeBQwFNAkgAIR5iACIAM4PfAAIAEIEKgIqCeoHKgG+AqAEhXmEAEQA5g9cAAQB8AGqAfgHrAGqAfgBAACNeYAAiADsDxgAwA98AMQH9ALUA1QI/AcAAI95iABIAO4HWACCAPoPagVqBeoHagV6BcIHpnkAACQFswWABesB/gHaB4ABvgOCAz4FAAWneQABigDqD5AABAB8D9wLXgtcC9wLfA8EAap5xAAkAPUPLQBAAv8CrQL/D6oCrQL/AgACrnmIAUoA6g+YAAAI/gv/CuoK/wrqCv4LAAixeQABigDqD5AAxA/8BfwH/gb8AvwP3AIAALl5AACED7wArASsBPwHrASsAqwKugiCBwAAunkAAIAP/ADUAtQC/APUAtQC1Aj8CIAPAAC7eQQABA90AWQFZAfWBVQFVANECXQJBAcEAL15EAAID+gBxAGUBbIHugWyB4QJ6A8IAAAAvnkgBCQCJAGkAGQA/A9iAKIAIgEiAiAEAAC/eQAAkAhUCFQENAN8ADQANAdSCFIIkASAAMB5iAhMCCwEnAOMALwAigqaCSoJSgdIAAAAwXkAApIBUgD+D1IAUgQAB/gEBgTABAAPAAjDeQAAUAhUCFQENAO8AJQANAdSCFQIUAYAAMZ5AAAYA5gA/A+UAAABRABEAPwPRABEAEAAyXkAACgEugS6AroBugH+D7oBugL5AigEAADLeQAAkgNSAP4PUgAACDgEgAN+AIADMAQICM15lANSAP4PUgACAPgBiACIAP4PiACIAPgB0XkAAJQDUgD+D1IAEAFCAUwBAAH+DwABAAHSeQAAlAFSAP4PEgCACDgEgAR+AgAB2AAAANV5EgOSAP4PUgAAAP4PIAQAAP4HIAgQCAgG2HkSA5IA/g9SAIAJYAT6B4IIYAgeCOAGAADfeRQDlAD8D1QAlAkACPwPJAkkCfwPAAgAAOR5AAAUA5QA/A+QAIQAtACEAPwPhAC0AIQA5nkgAawIbASsA7wDrg9sASwDbASsCCABAADneQAAFAOUAPwPkgAACPgIiAb+AYgG+AiACOl5lANUAPwPUgCSCBAETgJIAf4ASANIBEAI63mSA1IA/g9SAJICAAaQAXAA/g9QAJYBEAbveQAAlANUAPwPUgAACHwGRAFEAEQBfA4ACPB5kgNSAP4PUgCSAhACzwEECPwPBADsAQQC+HkUA5QA/A+SABoASA9ICUgJfglICUgPCAD7eQAAlAFUAPwPUgACCagErARWBXQCzAFAAP15FAOUAP4PUwAACZ4JUAhwBV8GUALQAV4AAHoQA5IA/g8SAAAB6gesALwApA+sAKoEqAMFehIDkgD+D0IACAj0BJIDkACSD+QICAgQBAt6AAAYA5gA/A8AATwIpAqkCqQPpAq8CgAIDXqSAVIA/g9SAAAA8gdUAVABXgFQBfYHAAAOegAAlANSAP4PAgA4DMoDSADMD04IeggABhp6AACUAVQA/A9SABAA/g+oBPwHqgSoBAAAIHoYA5gA+A+UAAAM/AMEANQH9ALUCwQI/AcjehAA+AVWBfQFXAX0BiAHpAD8D6QAIgcAAC56AADKASoA/wMACP4Krgr+D64K/QoACAAAMXoUA5IA/g+SAAAC6A+kAqQC6gOiCroK4gcyehQDkgD+D1IAEgDADxIAwg9aBUIF2g8AADN6JAIkAfwPogAABlgAVgVWC1QJXAHwBQAIN3qUA1QA/A9SAAAJfgmWBdYGvgZWBVYIXgA7eooBSgD+D0kAAADKB6oEgQQtBKEE7QcAADx6FAOUAPwPUgBABVwFtAJ2CbQG1ABcA0AEPXoUA5IA/geSABAAdAesB64HtAe2B5YHVAA/eooDSgD+D0kAgA9eAFYPVgVXB14Awg8CAEJ6FAOSAP4PUgCABnwArAysCf4KrAKsBvwIRnoSA5IA/g9SAIAIvAwsBSwF7gYsArwCgABMehAM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fXwsA6AA/g+gAAwBQABECEQI9A9MAEQAQACJfCwDoAD+D6AALAlgBFgGxAFACEYImAcgAJJ8LAYgAf4PoAAsAQAIaAiICw4IiA9oCAgIl3wIACAD/A+gACwBAAj8DyQJJAkkCfwPAAiYfAAALgOgAP4PJACAD4AE/gSIBIgEiA8AAJ58jAMgAP4PhAH8DxQE/AUUBPwEFAX0DwAAn3wECXQF1AXUBXwDVA9UAXwDVAVUBXQFBAmkfAAAgAD8AMQD1gLWAtQK9ArEDvwCgAAAAKV8AADyCJIIngc8A/4PKAAEA3AIkgieBwAAp3wsB6AA/g+oAAAM+AMICEgI7g9ICEgIAACqfAAAKAqoCqoKnAu+AogCmAacB6oKqAooCq58JgegAP4PoAAuAQAA/A9UBNYAVAN8BAAEsXxkCREFQQWMAyABkg8uAUIDwgV+BQwFMAm5fAMA0AH/D1AAkwGAALoAggDjB5oAkgCiAL18AAa4Af4PIAAIAwQIpAanAKQPpACMAoAMvnwCACwH4AC+DwAA1A/cAtwC3grcCtwPFADKfAwAIAP+DywAgA9+BMgDAAz+A5II/gcAANV8BgCgA/4P6AAACKgGqgCsDvgArAyqAIgO1nwGANgA/wNYABAP/AB0A/QC9gL0AvQDIADZfAwAoAH+D6gAJAlABNwHAAisC7wKqAqoC9x8AA78AQQIHAW8BS4Dhg8cA7wDHAUsBQAA33wGAKAB/g+MADoA1g//CtYK/wrWCv4PAADgfAMAkAH/D8IC/wEBAlUB/wfXAz0BkQKBAud8qAH+D6AAKAjgC/wL/Av8D/wL/AvcCwAI73wDANAA/w/eAY0HvQClB+8ApQetAL0HAAD4fIAEhASYA5QB4wDQD0gARAF0AUACgAQAAPt8AAAABJQE1ALcCLQIlA9SAEoBQgKCBIAE/nwAAJAO3ACyDIgCAAD8AwABAAEAAf4PAAAAfQAASAd8ANIPSAEAAOQHJAgkCCQIfAgABgR9SAd+ANAPTAAAAxAATgCICQgICAz4AwAABX0AAEgHdgDQD0wA4AoECAQI/A8ECAQIAAgKfQAAFAhUBVQFfAH0CVYPVAEcBZQJJAokAAt9SAduANAPTADgCggIOATIAg4DyAQ4BAgIDX1IB24A0A9MAAAB+A8IAcgAPgDICPgHAAAQfRAOnADyDsgAgAIECEQM/AtECPwPQAgAABR9SAduANAPTAEAAOgDCAEIAf4HBAnkCQAEGX1IB3YA0A9MAGQBAAT+ByIE/gEiBiEMAAAafUgHWAD2D2AAQAsEB/wAhAQkBTwD4AQgCBt9SAd2ANAPTAAAC3AITgbAAUIITgjQByAAIH0gACoJagVqA+oL/wlqByoBqgWqCSALIAghfUgHfgDQD0wAxAoACAgH+AAuCCgI6AcIACJ9AAA0CBQFVAX0AV4JVA80AbQFFAU0CQAAJ30ACB4JgAXeAcAJogmSD14BUgUeBSIJIAArfRAJHAlQBV4FtAGQD04BFAWUBRIJCAoAAC99AAAACD4FagXqAX4Jag8qAaoFPgUACAAIMH1IB3YA0A9MAcAA/A9EBEQE/AdEBEQE/A85fUgHbgDQD0wAAAMgAKIPngSCBKIEng8AAEJ9SAduANAPTACAAogATgQ1BTQFTAlAAAAAQ30QDtwAsgZIAEAHSABoBloFzAQoBggMAABEfUgHbgDQD0wAAAMACPwPJAkkCSQJ/A8ACEx9SAduANAPTAAAA0IIJgkqCZIPKgkmCUAIUH1IB24A0A9MAKADBACkD6QEvwSkBKQPJABVfbAN7ACiBJgCAAD0BzQJLAnkCTQJLAngBV59SAd4ANYPUAAACygImAQYA84DCAQoCCgAYX1IB24A0A/MAMQDkADID9YEpATcBEQPgABmfUgHbgDQD2wBIACQD4wEogSkBIgEkA8gAG59AAAsCCwFlgHUCcwJgA9eAVIFEgUeCQAAcX1ID3YA0Q9MAAAJBAS8AycALAC8DwQIAARyfQAAkA78AJIGSAIAAFgDdgjQD0wA4AIABHV9SAd2ANAPTAAACxAIiAamBaQEiAawDBAAdn1IB24A0A9MAQAA+AeWCJYI/AicCJQI8AaTfZAMvAHiDNgAAAo6CEIJEgnqD4IJPglGCJl9QAduANAPTAAAAf4PAASoBP4FoAQsBQAEmn0AAEgHfgDQD6wDgAlUBFQDXgBUBtQIBAScfTAO7ACiDJgCAACMDKQCpwCkD6QArAKADKB9MA7sAKICmAQABNoCVAjUD3QATAKABAAArX1IB24A0A9MAEADEAD+D6gE/AeqBKgEAACxfU4HcADsD0AB/A/sA+QDJAKsCyQI/AcAALJ9SAd4ANYPgAL8DwwAvAPkBLQEBAD8DwAAv31IB24A0A9sAQAAvgeqAKoA6w+qAL4EgAPKfQABPgUuBb4F7gGuB6IBWgMaAyYFQgUAAM99SAd2ANAPTAAAAV4EQAD8D2QJcQhGA4gE0X1IB24A0A9MAAAFIASqAioI6gc+AaAGAADSfUgDdgDQD0wA4AOEAJQHfgVUBVwF1gcQANp9SAduANAPzADABZwCrAksCO4PLAO8BAAE4H1IB3gA1g9IAQgAoAeUAJwA1g+cALQHAADjfSAIvAziAtgCgAQgBH4DtAq0B7wBIAYAAOh9SAduANAPTAAAD/wHrAKsD6wCrA+8AoQP6X1IB24A0A9MAEAJFARcA/QJ1AbSBtwFVAjvfUgHWAD2D2ACAAT0BbwHtgW0D7wF8AUABPR9SAd4ANYPSAAAAvwErAKsAf4PrAH8AgQE+32QDtwAsgTAAZwE1AeUBAQA8AwOA/gEAAgBfkgHXgDwD2wAgAIgBGoDqgpqB74AIAcAAAR+RANfAOgPJgAAAfwHvAK8AvwHvAq8CvwNI35AAnwBfAD8B/wASAIYAXwE1ANUANACgAAmfkgHbgDQD8wAQAL2D5gHFgTwD5YIEAgAACt+EA7eAHEEbAMBCHYG4AksCLsK+wsmCgAILn5AB34A0A9MAUAA7A8EANYHdAVUBdQHAAAxfhAO3gCxDMwCAAD3DwAEzgMBBOgHhgiYCD1+kA7cALIMiAIADPwChAaWCaQLhAD8BgAAPn5IB24A0A9MAAAK/Aj8Bf4F/AH8BfwJEAhBfgAIMAq+CjwL/AM8D4gCXgZkB1wKhAoAAEp+QAdOAPAP7AHAB/QHHgT0A5AG/geQDBYCVH6gAy4A8Ae8ABAA/Ae2AtgPPwPQBBYOAABefpAO3ACyBIgCAAiqCeoHrwEKD+oJqgmABWp+AAd4AJQGUgEIADQH8gX6BdIF9AX0BwQAa34AAH4Kfgd/B/4PfgsYB/YC0gp+CpgKgAhwfkgHbgDQD0wBAATwAt4B+gfqAdYC8AQAAHN+kA7cALICCAk+DOsD6wm+BzQMiwN4DAAAfH4QDtwAsgZIAv4PNAi6DzgJPgisDxALAAiMfpgO9gCRBkgBEgjuB/4H7wP+A+4H+gcACI9+gA7cAKIGWAEADPwD9An+C/4O/Av8C4QJln5QB3wAagIACXYE9QcQBPYPMAT+B9AMFgKgfqAEsAVsBSIFkAQAAPwBAAEAAQAB/g8AAKJ+AAAwCewEogSYBgAABAQEBPwHBAQEBAQEpH4AADAJqAVmBRAFQABEAEQA/A9EAEIAQACmfqAEuAXkBJIEgAIwAI4AiAgICQgM+AMAAKd+IAW4BeYEkAIQDIQDfAiEBTQCrAVgCAAIqn4AACAFuAVmBRAFAADEB0QIRAhECPwIAAarfgAAMAXoBaYEkAIACHQEBAP8CAQM/AMAAKx+IAW4BWYFEAQAAqgAqAD+D6gAqASIAwAAr34gBbgFZwUQBQAE9AEEAQQB/wcECfQNBASxfgAAMAXoBaYEkAIACHgIAAT+BAACmAEgALJ+AACYBdcEsQQAAP4PAgCyAdoBAgj+DwAAs34AADAFzgUiBQAA8A8QA/AAnggQCPAPAAC1fgAAMAXsBaIEkAAADv4BAAwAA/4AAAcACLd+AAAgBbgFZgUAAWAIWATEA0AIRgjYB2AAuH4gBbgFZgUwBQAA/A9ECEQE/AFEBkIIQAa5fgAAMAXsBKICmAoACDgEygIOA+gEGAQICLp+AAAwBawFYgUYAQAICAf4AE4ISAjIBwgAvX4AADAJrAVjBRgBAghCDP4LQghCDv4JAAi/fgAAuAXkBJICgAoQCFAEfgTIAyoFqggABMN+uATkBLICAABUBnwBVgj0D0QARAEEBgAAxH4AACAFuAVmBRABAAj8DyQJJAn8DwAIAADFfgAAIAm4BWYFAAD4AygBKAH+DygB+AEAAMZ+IAWwBW4FIgUAAPwPRAREBPwHRAT8DwAAx34gBbgE5gSQAgAIfAZEAUQARABEAXwOAAjIfgAAMAXoBaYEkAQQAEgCVgQkBVQFTAiAAMp+MAqsCWIJEAUAACwBIAH+DyABLAEiAQAAzX4wBZwE8gKIAiAAog+aBIYEogSiBJ4HAADOfiAF+AWmBJAEBACsAqwClA+0AqwCpAIAAM9+IA24BeYEkAAACKQIpAiUD6wIpAggCAAA0X4AADgL5gmQAQAIKA3+AwAA/A8EAHwChAHSfiAAmATkArICCABABugBCAh+BIgHaggIBtN+MAXsBKICmAIAAKQHpASkBL8EpASkBwAA1X4gBbwE4gSYAgAIpASmA7wAtA/ECKQIAATYfjAF7AWiBIACIAiQDqwJpgSIBpAEoAgAANl+IAW4BOYCgAIgAKAPsA+uBKgEsA8gAEAA234gBLgFZgUSAkAAqAKoAq4ClA+sAqQCIALcfgAAMAXsBKICkAIQAEgPtgSkBLQETA9AAN1+IAWcBOIEmAQAAPgHlgiWCPQInAjwCAAG3n4gCbgFZgUQAUAIOAiYBQoCyAUoCEgIAADffiAFuAVmBZIEEAJACGgHXgBID2gISAhIBuJ+AAAgCbgFZAUAAOAPvAK0ArQKtAr8DwAA434wBawE4gSYAgAIrAScA4wAvACaCSoHAADlfjAF6ASmAqACCAhEBeQGagLCBVoMQAQAAOd+IAC4BeYEsAIAAPgHBASgBP4FYASsBQAA6X4wBZwE8gKYCgAI3AVcBF4DXABcBNwJEADqfgAAmAT2ApICAAJUANQHfgVUBVwF1AcSAO1+IAW4BOYEkAIAALQIFAUeA9QBFAVwBRAI7n4AALAF7ASiBDgAoAe0AqwCrgs0CNQHNADwfiAFnATiBJgCAAD4AqgCqAKuD6wC/AIAAvN+AACwBWwFIgUAAPwDtAL0B7QKtAr8CwAA9H6wBOwEogKYAiAA/A+qBKgE+geoBKgEAAD1fiAAmAXWBLECAAC+B6oA6w+qAKoEvgMAAPd+IAX8BbIEAAz+A5II/gcADP4Dkgj+BwAA+H4AALAF7ASiAgAI/AcEANQH9AIECfwHAAD8fgAAIAW4BWYFAAUYAIgCqgisD6gAqAKABP1+AAAwBegFJAUAASgMqAMoBO4PKAkoCQAA/34AALgFZAUSBYgAQAQqAioJ6gcqAb4CoAQAfwAAOAqkC3MJAAyqCJoGBAmgCZIGnguiCAV/AAC8BeIEmQIIAOIHEgTyB14F8gfyBwAABn8AANgCTgJ5AQAE7wQ/AqABKAMmBOwFBAQOfwAAOAXmBJEAAAL8D1ICAAjeBUIC/gVgCBN/IAW8BOMEmALADFQC8gnWBtEG3QVVCAAIFH8QBawFYgUYBQAAtAecANYPnACUBLQDAAAVfwAAOAXkBLIECAKgCKoLmAbeBJgHmgSoCBZ/AAC4AuQCkgAABvwBlAfWA9QHVAHcBwAAGH8gBLwFYwUZAAAFbAVrBaoKqge+ASAGIAQZf5AE3AKjApgCAgDqD2IFfgV+BWIF6g8gABp/IAWwBG4FIAUAAHgBeAV8AXgJ/A/4AQABHX8AANwFswSIACII4AcECK8K6guuCqIKAAggfwAAOAXkBRIBAAz8A3QIrgr+D6wK/AoACCR/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SANABAAA/A8EAPwDK4EADP4Dkgn+BwgA9AySA5AAkg/8CBAIAAQvgQAM/gOSCP4HAADoD6gCqAL8D6gCrgroBzGBAAz+A5II/gcAAHgESgfMAMwPSgh5CAAGM4EADP4Dkgj+BwAA7g8ABEEFzgQABO4PAAA4gQAM/AMkCfwPIASQBSgEpgQoBxAHoAQAAD6BAAz+A5II/gcAAPwB1AFUAX4HVAFUAXwBSoEADP4Dkgj+BwAAFADeB1QFVAVeBdQHAABLgQAM/gOSCP4PAAD0DwQA5gxUA5QFdAgACEyBAAj+B5IA/g8AAOgDuALsB6gK+AsoCAAEUIHADzwApA+kALQGhAWmBZQF1Aj0CJQPBABUgQAM/gOSCP4HAACkCJQIhg+UCKQILAgAAFWBAAz+A5II/geAAEwItAdGAPQHFAj0CQAEZYEADP4Dkgj+BwAB/gjqCqoKqg+qCr4KgAhmgQAM/wPJAP8HAADvDyAEtwQoBSIE7QcAAG6BAAz+A5II/gcABHwDVABUB/wIVAhUAXwHcIEADP4Dkgj+B5IAqgf+BqoEvgOqBLoIAABzgQAI/geyAP4PgAD8D6AExA/4BwQA/AMAAHiBAAz+AxII/gcAAP4G6gHqBOoD/gjABwAAeYEADP4Dkgj+BwAAfAXuBewC7AX8BAAEAAB6gQAM/gOSCP4HAAC8AqwJLAjuDywDvAQABHuBAAz+A5II/geAAXgOeAD4BQgI/gMIDAoGfoEACP4HkgH+D4AB6gS4Bq4GqApqC6kGgAB/gQAM/gOSCP4PAAjuBwAI/AtUCNQJ/AoACICBAAz+A5II/gcAAFQO1AFUCVYJVAlUBwAAioEADP4Dkgj+BwAAeAV4BXgBfAl4D3wBeAGPgQAAZAA8D/wHvAe+B7wHvA/8DwwPZAAAAJiBAAz+A5II/gckAdQF/AHUD/wB1AX0BQAAmoEADPgDCAD4CPgH/gP8B/wH/A3cDxgAAACbgQAM/gMCCP4HEADKBagFrgeoBcoFGgUAAJyBAAz+A5II/gcAAHQFdgf0AXYDdAV0BQAJnYEADP4Dkgj+B4AAVAVMAj4PTAJMBVQEAACggQAM/gO2CP4HAABUCqIKvgpABVQFAgI+AKiBAAj+B5IA/g9AALQGvgR0BQAATAwiAwABs4EACP4HkgD+DwAAVA9WC/wLVAvWC1QPAAG9gQAM/gOiCP4HEAD4B5YItA+cD7QP0A/AAMCB8AAOAPoPugP6A74DgAP2C9ILPgdYAEAAwoFAADwA/A/cA9wD/AOwA7wL9gu8BzQAEADJgQAM/gOiCP4HAADwB7gF9wBVDKQC6AwAAMqBAAz+A5II/gfgAN4G+gH6B+oH1gHwAgAE04EADP4Dkgj+D+QH/g90B/QFHgD8BJwHWAzjgQAA/geSBJIEkgSSBJ4HkgSSBPIEAgQAAOWBAAD8B6QEpAS8B+QEAAyAA3wAgAEABgAI6IEAAPwPlAScB5QEcAuYBKYHVA/UBPQPBADqgQAAAAD8DyQJJAknCSQJJAkkCfwPAAAAAO2BAAAACXwFXAVcA14BXAFcA1wFfAUACQAE84EECKQItAisCKQIpA+kCKQIlAikCAQIAAD0gQAAvASkBJQHlAS0AiAIfgSIA+gCGAQICPqBhAn8DdwP3A3cDd4P3A/cD9wP/A2ECQAA/IEAAPwPRARCBEIEAAREBEQERAREBPwPAAAAggAA1A9UBUQFRAUUBCQFAgViBXoFyg8AAAWCAAAcBfwF/AX6A/AB/AH8BfwF/AX8AwAABoIAAAAJ/AVSBQAFXgF0AVQFAAXUBTwJAAAHggAAAAn8BVQFAAU+AXQBAAVUBdQFPAkAAAiCAAAACfwFBAX8BSwBXAEkAfwFQAX8CQAJCYIAAMAAfAJaAsAC7gfcAsgCXAL8AsAAAAAKggAAJAD0B/QH9gfwBvAG9gf0B/QHlAcAAAyCAAAKAMoPSgRKBH4ESgRJBEkEyQcIAAAADYIQAFAAUAdYBVQF8gVUBVQFWAVQB1AAEAAOghAAUABID0QJUgl6CVIJVAlYCUgPUAAQABKCEABID0QF8wVEBQgHYABqCPIPLgDiAAAAFIIAAJIP/gSSBNAP1AE0CJwPFAA0AlQAUAMXghAASA9WBXwFWAcQAMAPqAL+D6gC6g8AABiCEABYD1QF8gVEDwwA9A9WBVQFdAUcDwAAHIIACbAIUgVWAtIBFgDSA1IC2Q9VAlACAAAeghgJWAn+BtQC/AHUAPwD1AK8B9QCkAIAAB+CQAhABvwBRABMAU4BVQJECEQI/A9AAEAAKoJADPwDRwBVCfwHAADoDygALgDoDwgEAAAsgkAM/ANOAEUN/AMACN4IQgVCBs4FUAgAADCCAABADPgD1Aj4DwAAfg4CAfoHAgj+CAAGMYJADPwD1gBECPwHEADoBycIJQnoCDAGAAA1gkAM/APWAEUI/AcAAOgHigiMCEgIWAgABjaCQAjAB3wAVgn8BwAA+A+IBI4EiAT4DwAAN4JADPwDVwFFDPwDAAhoBFgHzgRoBCgHCAg5gkAM/ANEAFcJ/AcAALgPhgSABI4EsAcgAEeCQAz4A14BSgj4D7QH7AQACCQK/AsiCgACWIJADPwDzwhFCPwHAAC+CKAH/wSgBrwFgAhmgkAP/gBbB/4BAAT/B9UE9wfcBI8HpASkB2+CAAAACPwHVARUBNYEVAFUAlQFfAWACAAAcIIUBCQCxAF8BgAA/g9SCNIIUgFSBn4FAAlygiAAEAD4B5QIlgiUCPQInAiQCPAJAAQAAHOCAAAoASgB/g8oAQgA+AeWCPQInAjwCAAGeoIAAAQAJAQuCiQJpAikCGQILghECAQGAAB+ggQIBAgkBM4EBAMEAgQDzgQkBAQIBAgAAIKCBAAkACQALgDkDyQAJAAkAi4C5AEEAAAAi4KEAJQAlACWCJQIlAj0B5YAlACUAIQAAACNgoQARAA0ALYAJAEkAiQIJAguCCQM5AMAAJKCBABEAMQHTgREBFQEZAREBE4ERAREBAQAl4IECAQJRApmCdQFVAdEAg4DxABEAAQAAACZgoQIpAikBK4EpAL0AaQBpAKuBKQEpAiECJyCggiSCJIIlwSSApIB8geSCJcIkgiSBIIAnYIECEQERAJeBEQMdApECkQJ3ghECEQIBAilgkQARAgkBK4DJAAUABQAJACuDyQARABEAKaCAAgEBvQBVgFUAVQBVAFWAUwBzAMEAAAArIKEAEQIxAiuBKQDhACECKQILgdEAIQABACtggAABAD0B5YIlAjwCJQIlAiWCJQI9AgEBq6CAgDiDyIALwIiAbIAcACiAC8LIgjiDwIAr4IIBIgDCACcBwgIKAhICIgIHAaIAAgHCACxggAABAGEAOQPDgAEAeQHjghECEQIJAgABrOCJAgkBCQGLgHkALQIpAikCK4IpAckACQAt4IACAQIxA8OCAQIBAjwD4QIjgiECAQIAAC4goQApAikCK4PpAykDKQMpAyuDKQGpAiECLmCBAgEBPQDlgCUAJQAkACUD5YAjACMAAAAvYIECJQE1ASWApQClAmUCZQI9geUAJQAhADHggQAFAFUAVYBVAH0D1QBVAFWBVQFFAMAAM2CRABEACQA5gdUCFQIVApUCuYJJAREAAQAz4IECiQJpAQuAqQBdAAkCCQI7gcEAIQDBADRgoQIRAi0BD4D5AAAAOQHJAguCSQJ5AkABNSCAABEAEQPZgVUBVQFRAVUBVYFZA8EAAAA14IEAAQA9AeWBJQE9AeUBJQElgT0BwQABADbggAAFADUB1YCVAJUAtADFAgWCPQHFAAQAN6CggBCACIHugqvCqIKogqiCy8I4gsCCAAE34KEAEQAJAe2AqQCoAKkCyQILggkDOQDAADlggQCJAMkAa4P5AS0BKQEpASuBKQHJAAAAOaCAAAkAKQPrgSkBPQEpASkBK4EpAckAAAA64IAAAQAhAeOBIQE9ASkBKQErgSkByQAAADxggAABAnkCS4FJAf0ASQDJAUuCeQJBAkAAPmCBAEUAVQBFgEUAfQPFAEUARYBVAEUAQQBAYMAAAQA5AaOBIQE9AeEBIQEjgTkBAQOBAACgwAABAzkAy4IJAgkBHQEpAMuBaQItAgkBgODhACUCCQETgMEAOQHJAgkCC4J5AgECAAEBINECEQG5AFOCEQIhAfkDyQELgQkBOQPAAAFg4QElASUArYKtAnUCNQH1AC2AJQChAGEAAmDJAikBKQErgKkAfQPpACkAa4CpASkBCQIDoOECJQIVAhWCVQJVA9UCXQJdglUCUQIhAgngwAABATkBS4FJAX0ByQFLgUkBeQFBAgAACiDBAAkAiQKDglEBDQEJAPkAS4CpARkCAQIK4NEBFQERAIOASQA5AckBDQEPgQkBCQEJAAsgxQBlADUDz4AlAiUCJQI1A+WCJQIlAgEADKDAAjEDKQKjglEBCQMgADkBo4JhAREBgAINIMEAPQHFATWBVQFVAVUBdQFFgT0BwQAAAA1gwQA9A8UDJYMlArUCZQJlAqWChQI9A8EADaDRAAkDKQCpgKUANQPlACkAK4CpAIkBEQAOIMEABQEFAT2B1QFVAVUBVQF9g8UBBQEBABGg4QIlAT2A5QAlAD2D5QAAAD8BQAI/gcAAEmDAAAEAvwCrgKsAqwPrAKsAq4C/AIEAgAAT4MAAIQB5A8OACQJJAkgCeQPLgkkCSQJAABQgxQBlADUDzYAFAFUCVQJ1A9WAVQBFAEUAFKDBAAUCHQElgKUAJQOlACUAJYOlAgUCAQEVIMAAEQJVAVWAzQJGAcQAFQFVgM0CQQHAABYgwQEZAIEAu4PBABECEQI5A9OCEQIRAgEAFqDggiKBKoEiwKKAfoAigGKAosCqgSKBIIEXoNEAkQBVAnWCFQGdABUANQPTgFMAUQCBABgg4QAlAiUDJYCdABUAFQAdACWD5QAlACEAGGDRAhUBEQDDgBEAlQJ1AR0AtYBVAjEBwAAY4MACLQIlASUBJYC1A+UAZYClASUBLQIAAhkgwQEVARUBdYFVAVUBVQPVAVWBVQFVAQEAGWDAAS0DJQElAKWCRQI1A8WAxQFlAQ0CAAAZ4MAADQElAQWAhQB1ACUABQBFgKUBTQEAARrgwAA9A8UAHYClAkEBPQDVglUCVQJ9AcAAG+DRAl0BcQFTgUEBUQAJAC0ACYJJAjkBwAAd4OAAEQA9A8WANQHVAJUAtQLFgj0BxQAAAB4gwAAdAI0ArYKtAq0CrQOtAO2AjQCdAIAAIaDAAAUAPQPtgK0AvAP/A+0Ar4K/AcUAAAAiYNEBFQCVAH2D9QAVAMEAOQDDggECPQPAACKgwAJdAdEAe4PBABECEQIRAjuD0QIRAhACI6DBAAEAFQIlgYkAoQIZAgEBO4EBAIkAsQAkoMAAAQAdAdWBVQF0AVUBVYFVAV0BwQAAACTg0QBJAGcB14FVAXUB9QHVA1WDdQHFAUAAJiDAACUApQC9gKQApgPkALUArYClAKUAgAAnoMACLQI9Aj2BvQB9ADwAPQH9giUCLQEAACrgwAIBAr0CrYKtAa0A7QCtAa2CvQKBAoAALGDhAiUBLQElgKUAfQPlACUAZYCtASUBIQIsoMACFQExAcGBVQJdAlUCdQLVglUCRQJBAC3gwAARAo0CdQHFgBEDEQD5ABOA0QEVAgEALmDAAB0CBQJVglUCdQPVAlUCVYLFAk0CAAAuoMAADwC7AMuA2wDfAM8AywDLgtsCRwHAAC9g1QAVAlUCTYFlAMUARQBlA8+AVwBVAFEAMGDAABUDPQD9gP0A/QD8AP0A/YL9A9UAAAAxYMAADQAFAD2B7QFtAW0BbQFtgXUBxQAAADHg6QI5AQ0A6YCZAQEAKQPpgT0BKQEpAcAAMqDQgBCCXoFLwMiA6IPIgMiA28JIgjiBwAAzIMEAPQPFAi2CrQJ9Au0CbYKlAoUCPQPBADPg0QAVAwUAxYA1ANQAVAB1AkWCPQPFAAEANyDBAlUCVQFFgM0A5QPFAMOA0wFLAkECQAA4INECFQOlAAGDPQDlAmUCvwElgaUCTQIBADpg4QAlACUDtYKlAqcCpwKlAr2CpQOlACAAO+DBABEBFQF9gVUBfQPVAVUBfYFVAVEBQQA8YOkCKQKtAp2BjQFNAU0BXQFtgu0CSQIBAjygwQCpAKkAq4C5A8EAAQA7g+kAqQCpAIAAgOEAACUApQCVgIQAhgPkAJUApYClAMUAwAABIRAACQBFAVWBVQF1AdUBVQFFgEUCPQHAAAKhAAAFA3UBBYCFAH4D/APFAFWApQEFA0ABAyEBADkB6QCrgLkCQQG9AFUAVYJ9A8AAAAADYRECFQEBAM2AXQBFAEUAfQPFgFUAVQBBAEOhKQJtAl0CzYLtAX0BTQFbgdsBWwJpAmkCR2EBAB0CVQJ1gT0BtQG1AT0AtYB1AB0AAQAJIQAADQIlAlWCVQJ1AdUBVYFVAWUBTQIAAAlhAAANAAUB3YFdAV0BXQFdAV2BRQHNAAAACeEIAC0DrQAtgy0APQPtAC0BrYA9A4kAAAAKIQAAPQPFAD2CQQElAO0ANQAngDUALQAlAAshAIAAg/6AasFqgX4B6oFqgWrBfoJAg8AAD2ERAhUBEQDDgCkAKQPtAlUCVYJtA6EAAAASYQUBRQFfAVeA1QB9A90AX4DdAVUBVQFAABXhAIBIgGqAKsPqgr6CqoKqgq7CqoPIgAAAFuEBAIEAXQH9gT0BvQF9AT2DPQI9AiEBwAAYYSAAMQA5A/eA9QD1Af0A9QPFggUCPQHBABjhAAAJAT0B/YH9Af0B/QH9gf0B+QHJAQAAGuEBACkD6QE9gSkAyAI9AcWAFQJFAn0DwAAbIQAANQK1Ap0BnYDFAJ0AtYCtA+0ApQCAAJxhCQIpAacAF4GNAjUClQKdAoeCPQCBAwEAHWEBAhUCVQJVgV0BVQDxANEA24FRAVkCSQAgoQAANQAVAdWAVQB1A9UAVQBVgVUBtQAAACLhHQCBAHkDw4ApAGUBVQBVgk0CbQPFAEAAZmEAAtcC1wH3gbcA1wLXAdcAV4DTARcCAAEnIQEAFQHVADWD1QAVAFEAlQI1g9UAFQHBASyhCQALAxEAxYA9A+0ArQC/g+0ArQK9AcQALiEkgSSBloAOwEKDWoBGgU7CWoAogQSCQAAvIQEACQM5APWD/AL6AvwC9QL9gvkDyQAIAC/hAQAFACUD/YA9Af0BfQF9Af2AJQIlAcEAMSEkACUALQH1gfUB9QH9Af0B9YH1AbUAJAAyYQAADQCFAFWD5QJVAlUCVQJlglUD1QDAAHLhAQARAjUDtYK1A70CtAO1ArWCtQMRAlEANaEAAAEAPQO9gr0CrwAvAa0CpYK9AoECAAE3YQEAAQIZA8OCWQPBAk8Dx4J1AmUDwQIAADmhEABRAX8BP4G/Ab8BvwG/gv8CvwGRAAAAOyEFAhUBMQHDgjkC9QLtAu0C7YLtAs0CkQA7oQACLQE8gMDBPoJWAtaC/4LXwtaC/oLAAIRhQQAdAzUA9YB9ArUCNQJ9gbUBtQI9AiABBOFAAjEC3wLfgv8D3wHfAf+B3wLfAvECQAIF4UEDPQDFAhWBNQBVA1UAVQF1glUAFQOBAAahQQP9ABUDNYB1A/wAQQEpAEmCPQPJAAAACGFJAmkCJQF3gG0CZQJhA/2AdQFtAwUCQAAI4UECXQHRAHuDwQAVAWQBXQJVgk0DxQBAAErhQQIVARUANYB1An0BfQB9gH0A/QF1AMAACyFBASUB/QHlgQEAFQPdABWD1YAdAd0CIQENYXEDzQA9A92BXQHdAXUDX4ElAf0CDQIAAY6hQQAxAcUAMYD1APUA9QD1gdUBhQE9AMAADyFRABUBMQHBgB0AfQH9Ab2BfQG9A70BwAAPYUUANQPxAfuD8QD1A+ECO4EJAPkBSQIAABDhUACSgJKD+sLSgt4D0oLRgvnC1YPQgEAAkmFJAgkBvQAdgF0DXAB9AF0DXYBdAEUDQAASoUEBnQABAauCEQFVABUA0QGLgiECTQIBANphUQIVAwEAo4AhAX0BfAD9AX2A/QJhAcEAG2FAAAkCHQHdgdwD/AHdAJ2D3QHdAckDwQAcoUEAOQB9AHmB/wB7AEEDP4DTADMD0QAAAB0hUIKYgnSCkcFIg0CCPoPqw+qD6oJ+g8CCH6FAABkACwP7gssC3gPLAvsC64LLA9kAAAAhIUkBEwHRAAWAfQB9AP0Af4F9Af0AfQBAAGHhQABNAGEDwYAtA6wALQOhADeCCwH5AUgCJuFAAD0D1QFVgV0BwQAlALeApQP1AKUAoAApoUEDvQBtAi2A/QLtAO0C/QDtge0C/QHBACphQAA+g86AMMN6gMoCKoKrgq/D6oKqgogCKqFBARUAVwJ3gd0AQQM9ANeAFQA1A9UAEAArIWEAJQIRAUGBfQDtA+0AfQDBgVEBZQIlACvhQACdAPUA9YP9A/UD9AP9A/WD1QPdAEAALCFAgiKDf4F2wHeBf4B3gX+Bd8B/gWKBQAAyYW0BLQC9A+2AZQCBADUD/4K3Ar0CtQPAADNhQAA9AT0B/YF9AfwBQQHXAVeBVQHVAQAAM+FAAl0B0QB9A/2A7QG9AYeD/wEFAfUCBAE0IUUBlQGVAPOCiQHBAD0CVQHXgFUD/QJAATVhYoEqgL6D6sDqgECDvoB+wOqA6oJ+g8AAN2FBANcB1wH/gv8C5gLQAc2BxQHdAqEAkQA5IUEDPQDVAn2DwQBdAX0AH4PZAL0BWQJAADlhQAItAXkBQYF9AGwD7QB1AN+A2wFBAUAAOmFIgRqAwIBNwCyD/IKsgqzD6oK6g+iAAAA+4UCACoMSgMLBeIDvgN2D5cPdgO+BeIFAgUHhiQM5AF8Df4BdA3kBAACVAH2D0wBTAYEAAuGRABkC0QI7gVkAnAI9Av2CvQC9Ab0BwAIEYYCCPoHCgRrBWoPagsKC2oLawtqDyoBCgAthgAAAAD+D/8H/gLgB8AC/gP/BT4A/gcAADiGBAD0D/QGfga0BvQLJA7+AH4F/AN0ARQFToYAAAAM8AMQCFAHfgHUAbQHlAi0CLAEEABPhgAAAAz4AwgIKAk+B1wBXAVMCVwNWAMIAFCGAADADzgAiA+oCr4K3ArcCswK3ApICCgAUYYAAAAP8AAQBlAAfgbUCLQKtAiUArAGAABUhgAM+AMICCgJKAs+DWwFbAtMC1wJWAkIAFWGAAz4AwgLqAaoBj4JXAjcC9wI3AtYCggAWoYAAAAP8AAQClAIfg60CLQOlAi0CpAIUABbhgAAgA9wABALUAp+D1QItA6UCrQKMAgAAF6GAAAADPwDBAiUC38HtgO2A7YH5gcsCgAAX4YgAO4EqgSqAyYG8AEYDDgDXwBaB0oEGAJnhgAM+AMYAd4PvA+MCxgI8gCSCJIIkgcAAGuGAAAABPgEiASIBP4HiASIBIgG+AQACAAAcYYABPQFNAU0BfQHNAUsB+wHBAj8AwAMAAZ5hgAA+ASIBP4D+AIABAQEBAT8BwQEBAQABH2GAAAACNwLVAlUCfQHVAVUBVQF3AUACAAIfoYABPgJjgf+B/gGAAgEAPwPJABEAIQAAACAhiAAFAjKDwgEGALwCIgIiAT+B4gEiAX4B4GGAAj4CYgE/gf4DgAIeASCBQwD4AQcCAAIgoYAAPgE/gOOA/gHAAB0AUQBRAl8DMADAACKhgAA+An+B44H+AYACBgI6gwKB4gDeAQICIyGAAD4BIgE/gf4BgAEKAEoAf4PJAEkAQABk4YAAPgEjgf+B/gGAADyCJIIngcAAP4PAACVhogASgjqCVoJTgnqB0oFSgVaB+oHSgxICJyG+AWIBP4HiAT4BBICIgGiCWII/gciAAAAo4YAAPgJiAT+B/gGAAw8AAIOwAkOCHALIAykhiAEIgTSBVYFWgXSB1YFWgVWB9IFIAgAAKqGAAD4CYgE/gf4BgAMJADIAgAC/g8AAQABr4YAAPgJiAT+B4gE+A4ACPwPIgjiDyIIAADAhgAA+A3IBP4D+AoADMgIygj6D8gIyAgACMaGAAT4Bf4HiAT4BgAI/A8kCSQJ/A8ACAAAx4YAAPgE/geIBPAGCADoBwoJjAiICFgIAAbJhgAI+AX+B4gE+AoAAFgARAJTBkQJyAAQAMuGAAASBMoFSgVSBd4HVgVWBVYH1gUSCAAA1IYAAvgCzgP+A0gCMAP+D2IEkgRiBP4PAADZhgAA+Az+B/4H+AYACCQJJAm/DyQJJAkgCNuGAAj4Bf4HiAT4BgAAXgZIAf4PSAFIAgAE5IYAAPgESAT+A3gDIAC4D6YErASwBKAPAADuhgAEFATUBUQFXAXGB0QFXAVEBdQFFAgAAPmGAAj4Bf4HiAT4CgAA5A9kAvQPbALkDwAA/oYABHgE/gN4AwABlAj8B5AMfgKQBVYIAAYAh0AAXAD0BXQFfAX0B3QFfAX0DTQI/AcAAAKHAAj4Cf4Hjgf4ClAASAVuBdQPbAVEBUAECIcACPgF/geIBIgE8A5MCVQF1ANUBVwJQAkShwAA+AX/B/gGAgi+B0II8AsCCv4LIgoACBOH+AWIBP8HiATwBhoP5gUACCQK/gsiCiIKFYcAAPgEjgf+B4gE+AIwDM4DyA9OCHkIAAYXhwAA+AX8B4gEcAKMD1QAVAH0AVQI3A8AABiHAAR8Av8DxAd4DCsC/AEkA/4FAgL+BwAAHIcAABQEpAekBrQGtge0BqwGjAaUBxQIAAAhhwAA+ARIAv4DeAMABtQHXgVUBV4F1AcQADuHAAD4BYgE/gf4CgAA/A88Az4DPAv8BxAAR4cAAPgJ+ASOB/gGAAj8A7QC9Ae0CvwNAARJhwAA+AmOB/4HiAT4BnAAqgL4D6wC+gIAAEyHAAR4Av4DOAOAAP4PkAAkAAgB/g+AAAAATof4CYgE/geIBHgB4AdeBGoFagVqCP4HAABXhwAE+AWOB/4HiAfwCLwK1ArWD9QK/AqACFmHAAj4Bf4HiAR4AsAHvALUB1YB1AdUAdwHYIcAAPgJmAj+B/gKAAD0D1QK1A9UCvQPBABmhwAI+AT+B4gEcAL+D5ICDghgCWIH3ggACHSH+AmIBP4HiAR4AIAHfwKAD/4DEgj+BwAAdocACPgF/gf4BgAAfA1IA/wPaAN8BUgJAACChwAI+AX+B4gGcAT8B1YFPAL4DwQA/AMAAIOHAAj4Bf4HiATwDgQIVAbcAVYJVAlUBwAAjYcEAPwP7ATsAnwJgAd4AIgI/geIBPgGAAiehwAA+ATOA/4DSAIwAX4GqgL+AaoJggcAAJ+HAAT4Av4DyAJ2A34JWgVaBdoBWgV+BQ4JoocAAGIE6AfuBuQG4AfyBugG7gboB3QIAAi6hwAA+AmOB/4HiATwBR4J6g1+DyoBvgsACsCHAAj4Cf4H+AYACJQC9ALuDtQCBANUAgAAxocABPgCSAL+A0gCMAtkCHYH9AF2A3QNAATLhwAE+AX+B4gE+AQAAlwENAl8CTIAWgaQANGHAAj4Bf4H+AYACPQDdAN0A3YP9AMQAgAA8ocAAKALrAqsB6wHPgisA6wKvAesBqAHAAj5h4QEfAQ3Bb4FtgV4B8oHtgWyBboHNggAAPuHAAT8Av8DRAJ+ATsL2g9+A+oHKwdqCwAJDYgACPgF/gfwBnwPPAO8B+ADMAf+AAgDOAQViAAI+AX+B/gCAgA6D0oH3gFaD2oBag9AAB+IAAD4Cf4HiARwAiYPeAt0APoPUAR+AwAMIYgAANQF3AW+B78Ftwk/AJcFrQesBaQFAAAiiAAAqgWeBf4H/gX+Cf8A/gX+B54FqgcgCDuILAAXBN4FQAV2Bf8HfgVwBWQFvwc0CBIAQIgABPgHCAQIBPwHCgQIBPgHCAQIBPgHAARFiAAA+Af8B/oH+AMCABwBIAH+DyABLAEAAEaIAAggCbwEJAS8AKYPfAEkAiQFvAQgCAAATIiIAEgA5A8SAAAAJAAkACQIJAjkDyQAIABNiIgARADzDwgAIgwiAoQBIAgiCOIPIgAAAFOIQABEAPMPCQDIAf8HCADDBRAE8gcSAAAAVIgAAMwA8g8YAIYI9QeUBAAAJAjkDyQAAABXiEAARADyDxkCUALeA1QCVAYABPIHEgAAAFmIAADIAOQPAgBUD3wFdAdkACQAJAjkByAAW4gEAEIA+Q8IAPoD3wLaB/4CAgDyBxIAAABdiAAAJADyDwgAeglaCf4H+gUJBCAA5A8gAGGIAABEAPIPCAn4Ba4D/AOsDfgBAAjkDyAAYogAAIwA4A8cALwHnAeYB7wHvAQAAOQPIABjiIAAiABICMgPKAgaBDwEyACIA0gEKAgACGWIAAAIAYoA6g+YAEgBAAD+DyAAQADAAIAAaIggATQJtAS0B3QEPgT0BDQBtAK0BCAIAABpiAgBiADuD5wAQAkcCOQEFAOEAnQEDAgAAGuICAGIAO4PnABIAQAIiARIBEQCMgIQAQAAbIgIAYgA7g+cAEgBAADQABAJEAj+BxAAAABwiBQBFAV8BVwH3AReBNwEXAFcA3wFFAUQBHeIAAAEAnQCVA5UCfYIVAlUAlQGdAkECQAAgYgAAhAC9AK0DrQJvgS0AbQCtAZ0BRAJAAiEiAQBhQD3D44AZAEADCICogF+AKEDIQQgBIuIkAKIArwKgg6ICcgIjgmYAqoGygrKCqgIjYgAAYwA7A9YAQAA8AcsCSgJ6AsICvgJAASSiAgBiADqD5gAAAj8CSQJJAkkCSQJ/AkACJaICAGIAOoP2AAAAPgPiASIBP8HiASIBPgPnIgAAYoA6g/YAAAESAJIAf4PSAFIAkgEAACriAgBiADKD7gBAAz4A8gISAl+BkgGyAkYCK2IhAKkAqQKlA6MCaYIvAnWAtYG1AXEBbQIsYgAAYgA6g/wACAA/A8CABAH/gCQAxYMAAC0iAgBiADsD5gAAACoALgDrAKoCpgKqAagAMGIAAFUC9QHXgRUAVQDEAz+AhAD0gQUCAAGwojWAtYC3gquDp4JxgSAAZ4CwAbABf4JAAjFiKACrAKQCrwOgAmoBKgBvAKoBqgEqAgAAMaICAGIAO4PnABIAQAELAUgBT4FIAXsDwAAz4gABEQFfA18D3wJ/gl8A3wFfAV8C0QLAAjUiBQA1A90APQHbAVkBWYFzAbUAKQPJAAAANWICAGIAO4PWACIAAABxA+yCIgIsgjED4gB2IiABKwCrAqsDoQJ3gSEAaYCrgasBKQIAADZiAABiADrD9gAgAIqAeoHvgSqBKoEvgcIANyICAEIAc4PuAAAAOgPqAKoAv4PqAKqCuoH3YiQAr4CmAqYDr4JgASoBKgBvgKoBqgEiAjhiAgBiADsD9gAAAB8CVQJVAn8D1QJfAkAAOSIAAGKAOoP2AAADPwDRAC2ApQPlAKUAgAA84gAApwEhQK2DrQJ9gS0AbQCtgaFBJwIAAD4iAgBigDqD9AAgAT8BNQC1A/8D9QB/AKABPmIgAJEAnwBfAf8BP4EfAF8AnwCfAXEBAAA/YgIAr4CnAq+BpwF/ASAAZwCwAbABb4JAAQCiQAAxAD1D2wAAASUBN8HlAQAAP8HIABAAAeJgABIAO4HeAAABT4F/gW8ArwCvAW8BAQEEIkIAYkAyg+4AAABvgeqBKoFqgaqCL4HAAASiSACFAL0AgQOZAl8CT4JfAI8BnwFJAkABCWJAAGIAMwP+AAADvwBdARcATwJ3A8cAQAAKokAAIgB7A/YACAI7AcACPwLVAhUCXwKAAgyiQABlADUB7AAAAT4A+gD7AP4B+gD6AMIAkSJQAXEBfwD7A/8CcYJ/APsBewF/AlECQAAX4kAAIgA7g/cAQAJTAVeAVAPTAFeBVQJEAByiQAFDAV8A34PfAt8CQAJfgP8BfwL4AtACX+JBAD0D5QE1AQ8BBQEFAR8BJQElAT0DwQAgYkAAIIIugiqC/4GqgSqBL4GqgWqBLoIgACGiUQBXAG8D1wA3AjsC/wH/Af8B/wLPAgACIeJAgBeB9YH1g/eB1YHFgz+A7YCtgr+BwIAi4kACAAI/AlUBVQDVAFUAVQHVAn8CQAIAASPiUgISAT8A0gBSAoACPwFVANUD1QJ/AkABJOJAAgMCPwL5AbkBuwC5ALkBuIK+gsCCAAElokIAYgAzg+4AAgJAAj8BVQDVA9UCfwJAASaiQAIOAgKCOwL6AbqAuwC6A7sCwoIOAgABKeJAAg8CPwL/Af8B/wDwAPsB+gL6AsoCAAEqokAAKQErALmD6wCoAj8BVQDVAFUD/wJAASziQAAMAHsB7gH6AeoB/wNVANUAVQH/AkABLqJAAAwCB4I3gvQB9oD2gPYD94LHggwCAAEvYkACD4I7gv+B+4H7gPAA/YH/Av8CzQI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AAAAAPwProsgACQI7A8ABAQCeAiCBQwCgAV4CAQIAAiviyAAJAjkBwgCQAJEAPwPRAAEAPwHAAgABrCLIAAiCOQPCAQAAMQHRAhECEQIRAj8CAAGsosgACII5AcAAogIiAT+A4gAiAD+D4gAgACziyAAJADoDwAEiAKoAKgA/g+oAKgEiAMAALaLEAASBvQBBAWADDoCIgGiCGII/gciACIAuIsgACIA7A8ABLAAjgCIAPgPiACIAIgAAAC5iyAAIgjkBwgCYAD4DwYAgAD+ByAIGAgIBrqLIAAiCOQPCAIgAJAHCAmGCIgIkAggBiAAvIsgACII5AcIBGAAGA6GBWAEBgUYB2AIAAC9iwAAJAjkBwgCAAz8AyQGxAEkAvwHAAgABL6LIAAiCOQHCAIgCqAIXgVCAkIGfgXgCCAIv4sgACIE7AcEAgAICAb4AUoISghICMgHCADAiyAAIgTkBwgCQAlIBEgD/gDIA3gEQAgAAMGLIAAiCOwHCAIACOQPBAgECPwPRAhECAQIxIsAACII7AcEBIACugCCAP4PggCyAIoAAADFiyAAJAjoDwgEAAD8DyQJJAkkCfwPAAgAAMaLIAAiBOwHCAIACHwGRAFEAEQARAF8DgAIyIsgACII7AcIAmACEAAOAPgPKAEoASgBKAHJiyAAIgjkDwgEAAD8DyQAJAHkDyICIgIAAMqLIAAiCOQHCAQAAlAISAkGBcgEUAIQAQAAzYsgACII5AcABgIA6gMqASoB6gkCCP4HAADPiyAAIgTsBwQCIACiD5oEhgSiBKIEngcAANGLIAAkCOgHAAIkAqQCrAKUD7QCrAIkAgAA1YsAACII4gcCBAgASATIBwgC/gAIBwoMCALXiyAAJAjoBwgCoACoAqgCvAioCOgHqACgANqLAAAiDOwDAA74AygAKALICX4EiAfqCAgG3YsAACQI6AcIAiAApA+kCPwIpAikCKIPIADeiwAAIgjsBwAAsg0uA8IE8AkCCv4LIgoCCuGLAAAiAOwPAAD4DxYA1AdUCFwK0AkQCAAE4osgACQI7AcABBAAyAOuAqgK6AsICPgHAADliyAAIgjsDwAEJAG0CKwEZwQkBhQFhAgAAOaLIAAiCOQHAAIIAEoBSgH4D0gBTgEIAQAA54sgACQI6AcAAhgACAEoAe4HqAiICJgEAADriwAAIgjkDwAA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gAT/BJgEmA8YAL+MAAQQBPwF9APsA/AB9AHsA+QD1AUMBAAAwIwAABQIzAvmCvQK7ALgAv4K8gryCx4IAAjDjBAACADcA8QL4AvqA+oD/gvqC+kDKgAAAMSMAAj8DVQFVAH8BUAA+A+sAqgCqAroBwAAx4wQCBII9Av0BuQG9AL2AuwG9Ab0CxQIAADKjAAI/A1UAVQF/AFIAOgJSAR+AogFbAgIBtOMIAAsCOwH/AbsAv4C7AL8AvwG/AcMCAAI24wAABwIzAXuBewF7AHgAfwF7gX8BRwIIADcjAAI/A1UAVQF/AUAAb4C6gmqBKoDvgiAB96MAAAcCMYP9g30DfYF9AX0DfYNxQ8cCAAA4IwACPwFVAVUAfwFIAC0B6QEpgSkBLQHJADijAAIPgj6B/4H+gf6A+AD4gfaB5oHJgggAOOMAAQ6CO4H7gf+B+8D7gP+B+4H7gc+CAIA5IwACPwFVAVUAfwDqAjsBBgH2ga8CbgJEATmjAAI/A1UAfwFAAjUDxQI0AeQBP4DEAwUBuqMIAAcCMwL7Ab6BvgC7gLqBuoG2gsKCAgA7YwABPwHVAFUA/wFAAGkB/4FpAW8BaYHIAD0jAAEeAJYAfwPWAE4AMAL7ArkAvQG7AcACPqMAAD8DVQB/AIICGwF+A9oAfgPbgPoBUAI/IwACPwNVAFUA/wGwA/sAv4C7Av+CuwHIAL9jAAArAh8BPwH/AP+A/wD/AP8B7QEpAQAAAiNAAj8BVQB/AUAAPwP5wrkCvwK5ArnD3wACo0AACgI/g9eDVwFXAV8BV4FXgXsDxwICAgPjQAMggO2CjoHugm7A7oFOgC6B7oPCgQAAB2NAAAACPwJBAQEBAQC9AEEBAQE/AkACAAAHo0ACAAI8AsQCBAEHgLUARQEFAT0BQQIAAAfjSAIEAj4CRQIFgQUAtQBHAQQBPAJAAgAACGNAAAUCNQLVAhUBlwBVARUBFQI1AsUCAAAIo0ACP4JAgT6AwIA/g4AAAgDiAhoCP8HCAAjjRAAFAjcC1wIXARcA14AXARcBNwFFAgAACSNAAAeCMAFQAReAkABZgRaBFoI1gsSACAAJY0ACP4IAgb6Af4MAABwCI4EiAN4BAgIAAAmjQAEfwf5AAEC/wIACP8HIAToACYDIQQAACeNAAAICMQLXghBBkQBTwRUBFQI0gsKCAAAKI0AAPAHDADsCSwELAK8ASoEKgTqBQgIAAApjQAAfA/0AAQC/AAADvwBNAzUAhQD8gQECCqNCAAICNQLVAhSBFoDUgB0BFQE1AUECAgAK40IACgI7AtqCFoESANoAGoEXATEBQgICAAsjQAI/g3yAwIC/gAADCQCIgYqCeIIIQgACC2NAAj8DeQDBAD8DhAAzAEKAcgJCAj4BwAALo0AAPwJBAT0AwQA/A4QAAgEDgQIBAgEOAQvjQAACAj8C2oIagZ+AWoEagRqCP4LCAgAADCNAAAoCLgJuAS4ArgIuAsICP4BCAYOCAgEMY0ACPwIBAb0AfwCAAhQBP4EUAdUCVAJAAQ0jQAI/A30AwQA/AYAAIAP/gSIBIgEiA8AADWNAAAgCPwLdAh0BHQDfgB0BHQE/AUgCAAAN40QAAgI3AtCCEgESANOAFgEagjiCxQAAAA4jQAAAAjcC1IIWwRAA1IATgRiBOIFHggAADmNAAhACN4LdghfBFYDVgBfBFYE1gUWCAAAOo0AABQI1AtOCGQGXAFABFwEVAjUCxwAAAA8jQAE/AL0AQQE/AAABugBCAh+BIgH6ggIBj6NAgg6COoFagR+BGoDagB+AmoE6gU6CAIAP40AAPwN9AMEAPwFwAD4D64CqAKoCugPCABBjQgABAjfBUAEagNqAGoCfgJqBOkFKggAAEKNAAj8BuQBBAT8BAAAyA/WBKQE1ARMD0AAQ40AAPwN5AP8DAAM+AMICEoI6g9ICEgIAABEjRAIEgjUBUAEaAJmA1YATAJUBNQFDAgAAEqNAAD8DeQDBAD8DgAAUANoCOYPaABIBxAES40AAPwN5AP8AAAI1A8UCNQHEAD+AxAMFgZMjQAA/A/0AQQE/AWAANQPfgVUBVgF1A8QAE6NAAj+BfIDAgD+BQAApAlUB/4BFAU0CQAAT40AABwIhgu0CLQEtgK0ALQEtgSFBRwIAABQjQAI/AkEBPQDBAD8DgAAvgDqC6oGqgm+B1SNAAj8B+QBBAT8BQAAqA+kBKYEtASkByAAVo0ACPQElAL+D5QB9AoACPgH1gMcCPQLAAhYjSAELgSeBV4EXgJeA0gAbAJeBJwFJAQkAFqNAAj8DeQD/AAACFoF/A9YAfgPXgX4CUAAW40AAKwIvAj8BbwEvgK8ALwE/AW0BCQJAABejSAALgicC04IfARAA24AXAROBJwFLAgoAGCNAAB+BAID+gACAn4AmAd/BXwFfwX8BwAAYY0AAPwN9AP8DAAA+AcOAOwPzAvIC9APAABijQAM4gO+Cv4H3gw/A/4M3gc+Af4PBgQAAGSNIAKkAaQIJAbkAT4IJAjkDyQApAAgAwACZo2ACSQE5AM+COQHJACACTQEygKIA3gECAhrjYAJJATkAz4I5AekAIAH5AE+COQHJACAA3CNIAgoBKgDKAQoBPwPKAkoCSgJKAkgCAAAdI0QBtQBFAL+A1QEVARQBAAE/gUIBBAEIAR1jRAG1AEUAf4DVARUBAAFzAQwBEwEggUABHaNIAykAyQE/gekCKQIAAgkCPwLJAgkCAAAd40QCNQHFAL+B5QIlAgACOQJJAokCjwKgAmBjRAG1AEUAf4DVARQBAQFUgUiBaQEiAQIBIWNAAjYBxgE/AeYCBAI1AtMCmQKZArcCwAIio0gDKgD/AeoCAAI+AmICAgKfgnICioKAAiLjSAGqAEoAv4DiAQgBBgFVgVUBVwF9AUABJWNIAaoASgC/AOoBQAFXATcBNwF3ATcBAAEmY0gDKgDKAL8B6gIAAj0CzAIvAiQCvQJAAifjQAIpAckAv8HpAgECPQLkAheCZAI9gsACKONFAAUBJ4D9AMQBP4EqgT+BQAE5AUcBQAEqI0QDNQDFAL+B5QIpAjvC14JSgluCN4LAACzjQAAAAg8BKQDJAQkCOQPJAkkCTwJAAgAALSNAAjeBxIE8geeAAAO/AEAAAQA/AAABwAIvo0ACN4HEgTyB54AAAj4DwAI/g9ACEAIAADDjQAI3gfyB5IEngQAACQMIgP+ACIDIgQgCMuNAAieDxII8geeDAAICAf/AEgLSgbKCUgAzI0ABN4HEgTyA14CAAhOBEgD/gBIA0gESAjRjQAE3gcSBPIHngQAAPoHtAj0CgQK/AkABNuNAASeDxIE8geeAAAM+APICX8GSAXYCAgI3Y0ABNwHFAT0A5wCAAD8DxQFFAUUBfQFAATfjQAI3gcSBPIHngAACPwPVAjUAVQGfAUACeGNAASeB/IHkgQOAuAM+AMOCPgHCADoAQAA6I0ABJ4H8gfyB44EIAC4A6gKrAq4CqgGqADqjQAAHgfyB5IEPgz4BxYA1AdUCFwK0AkQBO+NAAjeBxIE8geeBJAAiA/WCKQIvAjED4AA840ACN4H8geeBAAA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dAV0BfwPIASTjwAAfAJGAvQPIAD4D7QC8g+UARgI8AcAAJWPRAB0Ak4C5A9AAhAC9A60Cb4JtAJ0BRAJlo8AAHwBRgH0B0ABFAC0B7QF9gW0BZQHgACZjwAAdAJOAuQPFADuB7QK5A90BIoDeAQACJuPIAAkASwBPAEkAecPJAE0ASQBJAEgAAAAnI8AAEIFXgVWBdYFVgV3D1YF1gVeBQIFAACejwQAkg+SBP4EkgcQAEQBdAHGD3QBRAFAAJ+PAAT8A6QPpAS8BwAAWAFIAc4PaAFIAUAAo48AAEQJVAnHB3QBBAjwBJQC/g+UAvQEAAimjxAIzAfoAUAIEAf+AOAPCABaAcYPaAEAAKiPRABVCcYHdAEACHAG/gEAAFQBxw90AUAAqY9EAFUJxgd0AUAADgjkBwAAVAHHD3QBQACrjwgAWgnsB0ABHATyBIgEAARVAccPdAEAAK2PBADMD1IBegfWANYPAABUAccPdAFEAQAAr48IAFoJ7AdIAUgPawVoBwwAXQHvD0wBAACwjwAAAAz8A0QA1A9UCFQE1AFUAlQFVAlACLGPAACAAn4CIgbqBqoCKgpqCqoG6gJiAwACso8ADPwDbAnsCf4H7AnsBf4H7AXsBfwLQAm5jwAIIgTsAwgEAAgIC8gIPggICggK+AkABLqPQghGBMgHAAQECMQLPAgECgQKBAr8CQAIu49ICFIE1gMEBBAIEAgQCP8LEAgQCBAIAAi8j0IIQgTMBwAIAAqCCWIIPgjACAAJAAoAAL2PAAgkBOgDAAQECAQKBAr0CxQIDAgECAAAvo8gCCIE7AMABAgICAvICD4ISAiICAgLAAjBjyAIJgToAwAEJAgkCCQI/AsiCCIIIggABMKPAAgkBOgDCAQgCCQKJAr8CSQIJAgkCAAAxI8gCCQE6AMABDAECAmsCmgKKAooCggJAADFjyQIJAToBwAEJAgkCPQLJAgECPwJAAqACcePIAgiBOwDAAQICCgIyAgICggK/gkICAgIyI8kCCQE6AcACAQLxAg8CBQIFAoUCvQJBATOjwAIRgzIAwAE/AkECQAI/AsECAQJ/AgAANCPIAgkBOgHAAgQCtQJNAkUCVQJlAkUCgAI0Y9CCEIExAcACAAL/ggSCBII8gsSCBIIAADUjwAIQgTMBwAIAAv+CDIK0goSCdIKMgoCCtiPAAgiBOwDCASACEQIJAj8CwQIJAjECIAI2Y8ACCQE6AcACCgKKAnKCEwIuAgICQgKAADbjyAIIgTsAwAESAhIC/4ISAhICP4LSAhICNyPIAgkBOgHAAgQChQJ9AgUCPQLFAqQCQAA3Y8ABCIE7AMABFQEVARUBP4FVARUBMQFAADejwAIIgTsBwAEFAksCSYJ9AskCSQJBAkAAN+PIAgkBOgDAAQACfwIJAhkCOQIJAk8CgAI5o8ACE4EwAcEBfwEBgj8CwAI/AsECvwLAAjqjwAIJAToBwAI+AtICkgK/gtICkgK+AsAAOuPRAhEBMgHAAQACPgLSApOCkgKSAr4CwAI8I9CCEIEzAcABIgJaAgICP4LCAhqCIoJAAj0j5AI1AS0AwAE/AkECvQKlAr0CgQK/AsAAPePQghGBMgHAAgkC6gIYAj+C2AIqAgkCwAK+Y8ACCQE6AMIBGAICAv6CAwK+AsICOgIAAD9j0IIQgTEAwAE/AlUClYKVQpUClwKwAsACACQQAhCBMwHBAgACv4LKgpqCKoJXgpACgAAAZBCCEIEzAcABEgITgpICfgISglKCkgKAAgCkCAIJgToBwAEFAjUC1QKfApSClIK0gsQCAOQAAhECMgHAAScCEAK/gkACP4JQAqcCoAKBpAACE4MwAMIBOoIjgqICvgJiAiOCOoICAgJkAAIJAjoBwAIIAqsCWgIPgjoCygKKAogCQqQAAgiBOwHAAQiCPoLRggwCv4LAAg4CMAADZAAAJII9AaUBQAI9gsQCF4JEAmUCvILAAgPkAAIJAjoBwAELAgsC9wIXAicCqoKqgkgCBCQAAgiBOwDAAQSBaoIrgpSCuIJUgiKCAAEEpAgBCIE7AMABHQFVwXUBPwFVgRVBdwEAAQUkAAITgTABxAESAlUCFQK8glUCEgJUAkQCBeQAAgkDMgDEAQECHQK1ApUCtQL9AoECgAIGZAAAFQEfALYAwAE+AV4BXwFeAX4BQgEAAQakCAIIgTkAwAE+gmuCK4I+guuCK4K+gkAABuQAAgkBOgHQAguC/QIAAokCvwLJAokCgAAHZAACCYI6AcACEgK/gkICPwLEgjyCxIIAAAekAAA0gi8BpgFAAi8CrQK9Au0CrwKAAoAAB+QQghCBMwHAAh0ClQJ1Aj+C9QIVAl0CgQKIJAACCQI6AcABCAIrAuoCrwKqAqoCyAIAAAikAAIJgTgBwQIlAqSCq8K6guuCqoKCAoQCCOQAggmBOQDAAR8CVwJXAn+C1wJXAl8CQQJLpAACE4IwAcQCJQKVApUCP4LVAl8ChAKAAgxkAAIQgzMAwAE/gkCCOoJegnqCQIK/gkACDKQAAhGCMgHAAQQCP4LqAr8C6oKqAoICgAIOJBCCEQExAcQCHgKVgnWCHQI3AtQCnAKAAE7kCAIJAToAwAEnAhUCnwKVAlcCdQIHAgAADyQJAgoBOgHgAt8BWwN7A9sDWwN/A8ACAAAPpAACCIM4gMIBOgF/Aj8CwsI6ggEDOgPCABCkAAIRgTIAwAEKAluCdgIqArICa4IqAgICUWQQAhEBNgDAAX8CBQI1AnUCdQL9AncCQAIR5AACCQE7AcACP4Lagj+CX4Jagt+DMAPAABKkAAIRgzAAwQF/AgXCuQJDAhGCvQLVAhECEuQAAgiDOQDAAR6CXoJegn6C3oJegl6CQABTZAACCIE7AMABfwI1Au0CPYLtAj0C7wI4AtOkAAIQgjMBwAI4As+COIJegnqCT4I4AsAAE+QAAQiAuwDgALcBTwFvAW8BTwFPAf8BwACU5AgCCQE6AcABAgI+gu+CrgKvgr6CwgIAABUkAAIRgTIAwAE1An0CdQJ3gvUCfQJ1AkQCFWQAAhECMgHAAj8CtwL3AreCtwL/AqICgAIV5AACCIE7AMABO4IagpqCX8IagrqCi4IIARZkJAI9gaQBQAIuA6kDrwO9g+sDqwOpA6ACFyQAADRDLYCEAVCBPoJBgpcCPYLQgjBCgAAXpCQCPMEFAOABH4IAgvyCD4Kfgh1CXUKMAFgkAIIQgjCBwgEfAlcCdwI3gtcCfwJiAoACGOQAghCDMQDEAT8CfwK/Ar+CvwK/AqQCwAIZZAACCQM6AMABEwJfApkCuwLZApiCmoLQABokIAI1ggwBwQG9Al+DVQNRAsQCI4LeAoICmmQAAhGDMgDAAT0CTQI/Al2CfwJNAr0CQAIbZAACBEE9gMABP4Jvgq/Cr4Kvwr+Cx4IAARukAAEJgLoA4ACfAQUBXQEVgZUBXQEFAUAAHKQkAjUBLQHAAb8CQwItAqMCtwLjAq8CgAKdZAACCYI6AcABHwJbgtsCWwJfA3+D3wJBAF3kIAI1AS0AwAF/AjsC/wM7A78D+wMfAkACXiQAQgmBOQDgASuCu4K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SgJABAAAyJEAAJYBUgD+B1UAAAb/ASEA4QAhAz8MAATKkQAAKgOiAP4PowCqAKACrgKSD6oCpgIiAsuRBACsAfwPogCoAD4C+gK+AroHvgL6Ai4CzJEAAAAEvgSqBKoEqgT+B6oEqgS+BAAEAADNkQAAEAT0BfQF9AX8B/QF9AXyBfIFEAQAAM6RAAB8CVQJ/AdUBXwBAAgkCPQPLADkAAAAz5EAACAE/Af8B/wH/Af8B/wH/Af8ByAEIADRkRAAsAiQCZgLlgjxD5IIlAiYCpAIMAgAAN2RUAhYC1QI8gdUBEQFAAAgAP4PIAAgAAAA45FQCFgLVAjyB1QERAUQAM4ICAkIDPgDAADnkUAIWAtUCPIHVAYADP4DAAD8BwAA/g8AABWSUAhYCfYHVARUAQAIRAj8D0QIxA98CEAAHpJQCFgLVAjyB1QEWAUGASUJJAkEBfwDAAA0klAIWAtUCPIHVAUQBEgA1w9SAEwE0AcQAESSUAlcCPIHVAREARAETgRIA/4ASANIBEgIcZJQCFwJ8gdUBAAN+AMIAAgH7gQIBIgHAAh0kkAIXglAC2AJXgnQD1YJZA00CVQJRAgAAICSUAhYC1QI8gdUBwAA/g9KCMoBSgZ+CQAIg5JQCFwJ8gdSBAQNZAQ0AywAJgC0DzQIRAaFklAIXAnyB1IEhAT+DwIA6gMqAcII/gcAAJiSWARUBfIHUgREBRACGAGUD+YEnASEDwAArZJQCFwF8gdUBFQBAAioBP4FqAaqCaoIAAazklAIXAX2B0AFEAD8BJIGkAGSD/QIGAgQBLeSQAhQCVwE8gdEBggN9gcQAF4BEAn2BwAA0pIAAFAIXAXyB1QFAABIBa8Fkg+uBaIFQATqklALWAT2B0gEWAUIAOgPqAL+D6gC6g8AAO2SUAlYBVQE8gdUBAABegzMA0gAzA96CAAG/JIAAFALXAjyB1QH/AsMBKQD5AMMC/wHAAAEk1AIWAn2B1QERAUABNgCVgjUD3QATAVABSKTUAhYBfYHVARUAQAIKAmeBRgHWgcqC6gIJpMoBC4F+QMqBioHgAe+AOsPqgCqBL4DAAArk1AIWAlUBPIHRAcABL4E6gOqBqoJvgcAAC+TWAhUCfIHVAQEBSQA/gdkBWQFfgXkBwAAMpNQCFgJ9gdUBAAFogQqAOoPqgEqAr4CoAQ2k0gLXAjyB1QERAEUAFQP1Ah+CNQDVAVACUqTWAhUCfIHVAQEBfQEtAKUAf4PtAH0AgQES5NQCVgI9gdUBQQA4A8+ALIDqgI+CeAHAAB1k0gIXAnyB8QHMAwuA+AEqgb/C6oKvgoICH6TaAhsCWoE+QdqBwIM/giuCv4Prgr9CgAIjJNIBM4E+QdKBewFrQL+D6wA/A+vAvwEAACWk1AIWAhUCfIHVAQACfQLsAq+ArAC9gsACK6TQAhQC1wI8gfUBQAA/AvsCu4C7Ar8CwAK4ZNQCFgIVgnxB1IECg3qCG4HawFuD+oJCAQYlFAIXAXyB1QFAADyC3oLcwvzD3ILfgvyCzWUMAhaCvwHHAd0ALQHvgdUDP4FEAfWCAAGUZRQCFwJ8gcEB34Aqg/uCboPAAlOD0gJSA9wlAgIXAnyB0QFCADoD8QHkgL0D4QCaA8AAH2UUAhcBfIHVA0ACBgI3gXcA9wL3gusCygAiJSYAIYI9Q+UBIQEIAAgACAA/g8gACAAIACJlAAAmACGCPUPlASUBAAABAgECPwHBAAEAJOUAACYAIYI9A+UBKACHABKAIgJCAj4BwAAmZSYAIYA9Q+UBJQEAADyAIIA/giSCJIHAgCdlJgAhgj1D5QEAAToAQgBCAH+BwgJCAnoDZ6UmACGCPUPlASEDIAIOAgABP4DgAEIADAAn5SYAIYI9Q+UBAQE8AGQAJAA/g+QAJAA8AGglLgAhgjlD6QEBADwDxACkAF+AJAJ8A8AAKKUmACGAPUPlAQAAP4PAgIyAfoBAgj+BwAApZS4AIYI5Q+kBKQAAAz8AyQBJAkkCfwPAACmlAAAmACGCPQPlASEDDAIDgboAQgDOAQICKeUmACECPYHlASEAhAATgJICUgJCAz4AwAAqZSYAIYA9Q+UBAQEEAHuAQgJyAsICPgHAACulJgAhgj0D5QEhAwACEIO/glCCEIO/gkACLGUmACGCPUHlAQAAsgISAT+BEgHKgmqCQAEs5SAAKwI6geoBIACEAD8D5AEkASQBPwPEAC7lJgAhgT1B5QElAIEAIAPgAT+BJAEkA8AAL6UmACGCPUPlAQABP4BkgCSAP4PkgCSAP4BwZSYAIYI9Q+UBBQEwADODMgC/gFIAkgEQAjDlJgAhgj1D5QEhAQQAEgBVgJEDcgAEAAAAMWUmACGCPUHlASUAgAAvg+CBIIE/gTADwAA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AAT+AtAI3gfQAPYCAASblgAA/w8ZAO8AbAa3AK4EoAenALgCrgIiAJyWAAD+DwIAOgHGABAC9AL8AvYP/AL0AhACoJYAAP4PMgDOCQQEbAFkDWwJZAtyCeoBAAyjlgAA/g8yAM4AAAnuBZgDvwCYA+oPqAIAAKeWAAD+DzIAzAhABLYHgASqCfgMyAvOCCgJqJYAAP4PMgDMCOIHFATUC+wI7gl8DewPBAiqlgAA/g86AcYIUAS4AvQAEwj0BrgE6AwIALGWAAD+DwIB/gDADfYB8gf6C/IN+QH1DaAItpYQBFQFVAVUAlQK/gdUAVQCVAO8BJAEAAC7lhAIEAl4CXwJeAd4BXwFeAV4B3gJSAgACL6WAAAUBqQB5AAcAyAA/g+oBPoHqASoBAAAwJYAAFAAWADED+AK3grwCvAPxArECtgKEADBlgAG/gFCAPoHAgAiAPoHUgX6B1IFUgUAAMSWCAzoAx4IyAcIBCAD/AeqBKgE/AeqBKgExZYkBrwBZAj8ByQA8A+uBKgE/geqBKgEAADGlggICAV8BV4DXAPcD34BXQNcBVwFRAkAAceWAAAEDvwBrADsD+wK7ArsD+wK7Ar8CgQIzJYAAPAPAAT+BwAE/gcwAv4PqAT6B6gECATPlhAAuASmBLQE7A8gAPgPrgSoBPoHqAQIBNGWJAC0BI4ChA+8AkAEMAD+B0gF/AdKBUgF1ZaAD3wANAe0AjQJ/AcwAPwPqgT0B6gECATWlgAA7gSqBPoDrgLgBjAA/g+oBPoHqASoBNmWAAAICL8EvAS+BbwGiAK+ArwFvgW8BCQE3JYgAKQGlADGD7QCAAD+D6gEqgT6B6gEqATelgQIfAl8BaoDUg04AfwPqgSoBP4HqAQIBOKWgA/0ANQD1gLECPQHIAD+DygF/AcqBQAE45YAAPQJ1gX0A9YF9AUgAP4PqAT8B6oEqATolgQA9A8UAFQBFAD8DxQAVAFUChQI9A8EAOqWGADKCsoKygqKCr4KigrKCsoKyg8YAAAA75YACJgIygjqCcoGngTKBIoG2gnKCJgIAAjwlgAKGAmaBboFCgM+ARoJOgmKBxgBAAIAAPKWAAEMBWUFZQdFBV8FRQVlB2UHBQ0cCQAA85YcDMYDdgh2CX4HRgF2CXYJdglGCVwHAAD2loAAmACaAboFSgXeBcoFWg26C4oBmACAAPeWGADKD8oKygqKCr4PigrKCsoKyg8YAAAA+ZaAAJgA+gf6CuoK/grKCvoLegrKCRgEAAD7lgAAGADaB/oCygLeB9oK+grKCxgIAAYAAP6WAAFYC1wLXAvMBtwC3AJ8C0wLXAcAAAAAAJcAAFgAWg96AUoP3gFaD3oBSglYB0AAAAAElwAAHgAOCK4HhgHeBYYG1gLeCoYHHgAAAAaXAAhMCeYFdgsmCD4NRg32D3YNZg0MDQAAB5cYDNoDegn6B8oF3gXKB/oF+gXKB1gLAAkJlwAADAFmBeYG5gbGB94Gxg7mDuYHTAUAAAqXAAg4CYoLqgmKD74Jig+qD6oJCgs4CwAIDZcAAQwBpg+2C4YL3g+GC7YLpguMCAAAAAAOlxgCWgtKC2oLyg9uD0oPWgf6B0oLWgsYCBOXAAAOAA4I1guuCq4GBgKuBr4K5gsOCAAEFpcAAJgEigK6D4oCPgCKBroBug+KApgEAAQclwAAmAS6AroPugI+AL4H+gX6BfoFyAcAAB6XGADKD/oF+gXKBd4MCgj6C/oFygXYCwAIJ5cAAAAMTAPmD/YBRgMeCsYGdgPmC0wHAAEylxgIyg16CXoPygueAooOegv6CkoPWAIAADiXAABMB+YH9g/GB14DBgz2A/YC5grMBwAAOZcABuwBdQdtBW0FBQcvAuUCpQ+tAu0CoABClwAO7AFlBP0E/QZ1BOcHZQb9BvUG9QQgAEiXAAgMCeYL/gtGCa4PpgnmDU4LtgjkCAAAUpcQABQA/A+8ArwCvgK8ArwKvAr8BxQAEABWlwgC6AIOAugBAAD8D7wCvgK8CrwK/A8UAFmXEADcD9wC3grcDxAAWAlWCfQHXAHwAUAAXJcQCNwH3ALeCtwHAACIAqQK6geiAuoDgABelwABKAEoASgB/g8AAAAA/g8oASgBKAEAAWCXAAQIB3wHXgdcD1wAXgBcD1wHfAcIBwAEYZcADvwBBAQ0B3QHVA8GADQPdAc0B1QHAABilwAA8g8SBPIHXgVSBfIHEgQSBPIPAgAAAGmXAAAEAuQCvgK0AvQPtAK0Ar4C5AIEAgAAdJcEAu4C/A+uAuQCAAIwAP4PAAD+BxAICAZ2lwQA7gKsAvwPrgJAAP4HQgh+CEII/ggABouXAADkAq4C/A/uAgQKIAgkCb8PJAkkCQAAjZcEAO4CrAP+DuQCAABICcYJdAZEBegEQAiRlwQC7gL8D64CZAjgBwQIkAt+CNAIEAsAAJiXBAD+ArQC9A++AkAI9AcQAF4BEAn0BwAAoJcEAv4C9A+0Au4C/AaKA+gP6AGICvgHAACtlwQC7gL8D24A/g98CawH/ASsCPwIBAgAANOX/AKsAq4PrAb8BgAE7AeuBawPrAXsBQgE5pcAAJIAkgCSAJIA/w+SAJIAkgSSBIADAADnl6gAqAD8D6gAqAIAAXQMhAN8CAQI/AcAAOmXAAD0AtQC3g/UAvQCAACoAP4PqACoBIgD7ZcAAFAFUAVQBf4HAAQABP4HqASoBKgEAADzlxAAFADUB1QFVAVWBVQFVAVcBdQHFAAQAPWXIADoB2YFdAXkByAAEAEuBagICAj4BwAA9pcQANwHVgVcBdQHBACwB44EogSiBJ4HAAD7lxAA3AdVBVoF1AcEAPgPVANUA1QD/A8ACP+XlAC+ANUH4Af+B+4HzgfAB94HxgeaAAAAAZgACAQI/AusBqwGrAKsAqwGrAb8CwQIAAACmAQIBAj8DwQAAAj8C6wKrAKsBqwG/AsAAAWYBAEEAfwBBAEECPwHrAasAqwGrAb8CwAIBpgACPwHAAD4AwAA/g8AAPwHrAasAqwK/AsImEgIyAQkBBADBAj8C6wKrAKsAqwK/AsACBCYJAgkCPQPPADkCAAI/AusAqwCrAr8CwAAEZgAABQH9AAUAvQDAAj8B6wGrAKsAvwLAAgTmOgAiAD8D4gE6AIACPQHtAa8ArQK9AsAABeYAA74ASgMvAKoA1gI/AusCqwCrAb8BwAIGJgAAFgA1AdSAMQJDAT8B6wCrAKsBvwPAAAtmAAAfAhMBUwEfAcADPwJrAqsAqwG/AcACDuYEADeBRAEfwIUAZAA/g1WA1YDVgH+BQAEPJgAAHgGWAH8D1gBOAr8CawGrAKsBvwHAAhGmAAEvASsAvwPrAKcCPgJrAqsAqwG/AcACEyYQAxcA1wE3AdcCQAM/A1cCVwJXAn8DQAMTZggAawPtAVWBaQHDAD8B6wGrAKsCvwLAABPmAAM7AM8CK4JLAUkAvwLrAqsAqwG/AcACFSYAAzkAywIpgosBSQA/AusCqwCrAr8CwAIWJgAD/wAfAasAKwP/AEACPwHrAasAvwLAAhbmAQK/AvcCt4C/AsAAPwHrAasAqwG/AsAAF6YAACsBJgC3gGcAogI/AusBqwCrAL8BwAIZ5gADvoBKg+qB9oHgAcCCPoLrgaqAvoHAgxvmAAB/A3aARoJ+gO8AwAJ/AesBqwC/AsACHWYAAAECPQLFAgUBBQC3AEUBBQI9AsECAAAdpgAAAQIBAj8BwQAAAj6CQoE7gMKCPoJAAB3mAAAAAD8AxACEAEACPQFFATcAxQE9AkECHmYBAEEAfwBhAAACPoFCgQKA+4ACgIKBPoFepgADPwDAAD4AwAA/g8AAPwIDAbsAQwC/Ax7mAAESAQmApABAgj6BAoC7gEKAAoC+gQCCH2YAAASBvIBEgDyBwAC+ggKBu4BCgD6DgAAfpj8DwIA+gcKBPoCAAj6BAoG7gEKAPoOAAB/mAAA9ACEAP8PhARgAvoICgbuAQoA+g4ACIGYMAwMAuIJIAjvBxAA+gkKBO4DCgj6CQAAgpgwBAwD4gIAAt4HAAD6DAoC7gEKAPoNAASEmCIAKgjqBzYA4gAACPoECgbuAQoA+g4AAIWYAAjwB5AAngD0AQAI/AkMBOwDDAj8CQAIhpgQAFABTAJqDdAAAAj0BBQG3AEUAPQOAAiHmAAO/AEkDD4D5AYMAPAEFAbcARQA9A4ACIiYAACiCJIIige2BAAM+gkKBO4DCgj6CwAAiphIBFgESAP+AHgDSAjgBBQC3AEUAvQEAACRmCAAuAsgCPwFKAMgCPQJFATcAxQE9AkAAJOYAAAsCKwHvACqBwAE8ggSBt4BEgTyCQAAlpgABM8C1AHUD7IBAAryCBoG1gESBPIFAAiXmAAE/ATUAvwP1AH8AOAJFATcAxQI9AkAAJiYQAxcA1wE3AdcCQAM9AwUC9wIFAr0DAQMnJgACPIHGgGzBKoCEgj6BAoC7gEKAPoOAACdmKwAFAZeBXQFlA4cAPQJFAbcARQA9A0ABKCYBAr8C+wK7gL8CwAA9AkUBNwDFAj0CwAApJgACPwL/Av+C8wLPAPwCAoE7gMKCPoJAAComAAAAA78AQQE7AUsBfwHLAXEDPwHAAgABLGYAA7+AUIE+gNCAv4AAAPYBFYJUAncDQAAxJgEAPQN/AH8B6QP/gdmBPYD+gP6BwQIAATGmEAGygFqAGcG6wFiDP8DYQT9A2EH/wEABs6YAAAADv4BAgQaAqIB4gAaBwIA/gcACAAG0pgoBMoEDgLsAwAK/AcEAvQBBAL8BwAMAADYmHQN1AH8CdQH/APUASQN/AMUBuQB/AcADNuYUAhSBvoBQgBCAPoPQgBCAM4DFAUqCUgF3pgAAAQABAAEAAQABAAEAPwAIAdQCIgIiAbfmAAAEAAQCPgPtAiyCbQCtAb4CZAIEAgAAO+YCATkB9IE0gT0BgAO/gFiDKIFIgPiBAAI8pgIBPQHsgSyBPQGIAAcDIoD+AAIAzgECAj8mBAE/AeyBLIE9AYAAOwDLAHsCQQI/AcAAP2YCAAIBPwHugS6AvQGAAD4A5YE9AQEBPwE/pgQBPwHsgS6BPIGNAToASYA/A8kAOQDAAAFmQQA8g9aCVoF8gkAAIoM+gOIAP8PiQAAAAqZgACUAFwI/g/cCdwJ3AfcBf4NXAiUCAAAEJkQAJAAqAjuD9wLgAO2B9YHlgWuCKIAAAATmQQI8gdxBnUDIQSJBP8DWAAfAvgD2wYQABiZCAjsD1oJVgX0DQQAlAKUCPMHlACEAogEKJkQBPgHtASyBOwGHAD0D5QFlgWUBfwHAABSmQAA8A++BLIE9A5AAGoNegNvASoP+g0oBWWZMAAMCOoPCAQ4AAAO/AEEAAQA/A8ACAAGbZkwAAwIygcYAAAO/AE0DNQCFAPyBBIIAABumXAADAjqDwgEOAIACDAEDgPoAAgDKAQYCHCZIAAUAMoPGAIAAOgDJwAkAPwPJAAkAuQBcZlgABQAyg8YBEAA8AcsCSgJ6AsICvgJAARymTAADAjKBxgCCADiAyoBKgHqCQII/gcAAHWZcAAMCOoPCAQwAAQE/AeUBJQElAL8DwQCdpkgABQIygcYAggIoASuA7gApA/UCJQIAAR6mTAACgjlBwQCCApgCBQEhQOGAwQEdAhABHyZIAAUCMoPCAS4AoEI+geIAIgA/A+LAIAAf5kgABAAzg8YBIAClAj8DxAE/gKQBVYIAAaAmSAAHADKDwgEGACQBpgAlAjyD5QAmAKQBIGZIAAUCMoHGAIACFQJxAVsAkQDwgRaCEAAhZkgABQEygcIAjgA0A9IBQYFRARcBcQPAACGmSAAFATKBxACBAD8B/QHVQVWBWQFDAcAAIuZMAAMCMoHGAKQALgCbgQ8BWwJrAq4AIAAjZkgABQEygcYAwAIdAV2BXQD9AF2A3QFAAiPmTAAjg8oBBgEQAA+D7AK5A+cCoQKvA8AAJKZEAAcAOoHCAFgBN4F/gbeAv4G3gX+BAAAlpkAAAQA9A9XBVQFXAVUBVQFVwX0DwQAAACZmZAAVABUD7QKlAq8CpQKsgrSCtIPUACQAKiZAAC8ArwC/g78D/QP+A72DvIP/g7YAgACrJkACAAM/AFUAVQFVAH8DVQBVAtUCQAHAACzmQAG/gCqB/4CqgoAB/wHEAj+CRAI+AgABsGZAAb+AKoC/gmqC6oHAADWDAgDDgXRBBAIxZkABv4AqgP+C6oOgA3+AyIA4gAiAz4MAADGmQAG/gCqBv4CqgsAB/4PAgSiBXIEigUABdCZAA7+AKoG/g6qCaoHAACKCPoPjAiICAAI0pkABv4Aqgf+AoIKEAfMASoB6AkICPgHAADVmQAAFAjMBeYB5AXsAeAD/gHyA/IFHgMAANuZAAN/ANUDfwNVBcEDHAwkA/8CJAQ8BAAI3ZkABv4Aqgf+A6oPAAAYAPgHjgiICFgIQAb/mQAG/gCqBP4Bqg8AADwHigS4Bq4JIAgAAA6aAAb+AKoD/guKByAApAe0Aq4LNAjUByAAEpoABv4Aqgf+A4oPQACiCWYJ+g9aCeYNIAgTmgAG/gCqA/4GqgoKB+gIJgXyAyQH6AkICBmaAAb+A6oB/gmAD/wH7APsB+oDagX6BwAAMJoADP4Dkgj+B0QA9gXcC9YL3Af2C1QGQAA3mgAO/gCqAv4LqgcAAPQJagnSD2oP5g8gCEWaAAb+BqoB/gmCBwAA/AdUCuwLnAnECwAAVZoABn4H/gKqCQIHqA/6A94F2gf5AKkPAABXmgAG/geqAf4NggNwCLgG9gBUDLgC0AwAAFqaEAg6DP4D+gP6C9oDyAvsA9YLrAssBiQAX5oADv4Aqgf+DgAHfga6AP4PbAAUAywEAABsmgAAAAIEAnQCRAJEAkQCRAp8CEAHwAAAAG6aAAJ6AkIJQgj+BwAAEAyQA34AkAEQBhAIb5oAAnoCQglyCM4HAAD+DwAA/gcAAP4PAABwmgACegJCCkII/gcAAPwHEAj+CQgI+AgABnGaAAB6AkIKQgn+B/gPBAgUC+QIlAkECgAAc5oAAnQCRAlECPwHAABUDIgCiANUBBIIAAB0mgAAegJCCkII/gcAAPgPSABKAEgA+AAAAHaaAAJ0AkQIRAz8AzAISAVIAv4FSAR4CAAAeZoAAnoKQgl6CMYHEADOAykB6AkICPgHAAB7mgAA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" + } +} diff --git a/libs/screen/frame.ts b/libs/screen/frame.ts new file mode 100644 index 00000000000..046401267ac --- /dev/null +++ b/libs/screen/frame.ts @@ -0,0 +1,28 @@ +namespace control.__screen { + let __update: () => void + let __updated = false; + + export function update() { + if (__update) + __update() + __updated = true + } + + export function setupUpdate(update: () => void) { + __updated = true; + __update = update; + update() + } + + // low frequency fallback screen refresh + control.runInParallel(() => { + while (true) { + __updated = false + pause(200) + if (!__updated) { + __screen.update(); + __updated = true + } + } + }) +} diff --git a/libs/screen/image.cpp b/libs/screen/image.cpp new file mode 100644 index 00000000000..0583afe620f --- /dev/null +++ b/libs/screen/image.cpp @@ -0,0 +1,1554 @@ +#include "pxt.h" + +typedef RefImage *SImage_; + +#define IMAGE_BITS 4 + +#if IMAGE_BITS == 1 +// OK +#elif IMAGE_BITS == 4 +// OK +#else +#error "Invalid IMAGE_BITS" +#endif + +#define XX(v) (int)(((int16_t)(v))) +#define YY(v) (int)(((int16_t)(((int32_t)(v)) >> 16))) + +namespace pxt { + +PXT_VTABLE(RefImage, ValType::Object) + +void RefImage::destroy(RefImage *t) {} + +void RefImage::print(RefImage *t) { + DMESG("RefImage %p size=%d x %d", t, t->width(), t->height()); +} + +int RefImage::wordHeight() { + if (bpp() == 1) + oops(20); + return ((height() * 4 + 31) >> 5); +} + +void RefImage::makeWritable() { + ++revision; + if (buffer->isReadOnly()) { + buffer = mkBuffer(data(), length()); + } +} + +uint8_t RefImage::fillMask(color c) { + return this->bpp() == 1 ? (c & 1) * 0xff : 0x11 * (c & 0xf); +} + +bool RefImage::inRange(int x, int y) { + return 0 <= x && x < width() && 0 <= y && y < height(); +} + +void RefImage::clamp(int *x, int *y) { + *x = min(max(*x, 0), width() - 1); + *y = min(max(*y, 0), height() - 1); +} + +RefImage::RefImage(BoxedBuffer *buf) : PXT_VTABLE_INIT(RefImage), buffer(buf) { + revision = 0; + if (!buf) + oops(21); +} + +static inline int byteSize(int w, int h, int bpp) { + if (bpp == 1) + return sizeof(ImageHeader) + ((h + 7) >> 3) * w; + else + return sizeof(ImageHeader) + (((h * 4 + 31) / 32) * 4) * w; +} + +SImage_ allocImage(const uint8_t *data, uint32_t sz) { + auto buf = mkBuffer(data, sz); + registerGCObj(buf); + SImage_ r = NEW_GC(RefImage, buf); + unregisterGCObj(buf); + return r; +} + +SImage_ mkImage(int width, int height, int bpp) { + if (width < 0 || height < 0 || width > 2000 || height > 2000) + return NULL; + if (bpp != 1 && bpp != 4) + return NULL; + uint32_t sz = byteSize(width, height, bpp); + SImage_ r = allocImage(NULL, sz); + auto hd = r->header(); + hd->magic = IMAGE_HEADER_MAGIC; + hd->bpp = bpp; + hd->width = width; + hd->height = height; + hd->padding = 0; + MEMDBG("mkImage: %d X %d => %p", width, height, r); + return r; +} + +bool isValidImage(Buffer buf) { + if (!buf || buf->length < 9) + return false; + + auto hd = (ImageHeader *)(buf->data); + if (hd->magic != IMAGE_HEADER_MAGIC || (hd->bpp != 1 && hd->bpp != 4)) + return false; + + int sz = byteSize(hd->width, hd->height, hd->bpp); + if (sz != (int)buf->length) + return false; + + return true; +} + +bool isLegacyImage(Buffer buf) { + if (!buf || buf->length < 5) + return false; + + if (buf->data[0] != 0xe1 && buf->data[0] != 0xe4) + return false; + + int sz = byteSize(buf->data[1], buf->data[2], buf->data[0] & 0xf) - 4; + if (sz != (int)buf->length) + return false; + + return true; +} + +} // namespace pxt + +namespace SImageMethods { + +/** + * Get the width of the image + */ +//% property +int width(SImage_ img) { + return img->width(); +} + +/** + * Get the height of the image + */ +//% property +int height(SImage_ img) { + return img->height(); +} + +/** + * True if the image is monochromatic (black and white) + */ +//% property +bool isMono(SImage_ img) { + return img->bpp() == 1; +} + +//% property +bool isStatic(SImage_ img) { + return img->buffer->isReadOnly(); +} + +//% property +bool revision(SImage_ img) { + return img->revision; +} + +/** + * Sets all pixels in the current image from the other image, which has to be of the same size and + * bpp. + */ +//% +void copyFrom(SImage_ img, SImage_ from) { + if (img->width() != from->width() || img->height() != from->height() || + img->bpp() != from->bpp()) + return; + img->makeWritable(); + memcpy(img->pix(), from->pix(), from->pixLength()); +} + +static void setCore(SImage_ img, int x, int y, int c) { + auto ptr = img->pix(x, y); + if (img->bpp() == 4) { + if (y & 1) + *ptr = (*ptr & 0x0f) | (c << 4); + else + *ptr = (*ptr & 0xf0) | (c & 0xf); + } else if (img->bpp() == 1) { + uint8_t mask = 0x01 << (y & 7); + if (c) + *ptr |= mask; + else + *ptr &= ~mask; + } +} + +static int getCore(SImage_ img, int x, int y) { + auto ptr = img->pix(x, y); + if (img->bpp() == 4) { + if (y & 1) + return *ptr >> 4; + else + return *ptr & 0x0f; + } else if (img->bpp() == 1) { + uint8_t mask = 0x01 << (y & 7); + return (*ptr & mask) ? 1 : 0; + } + return 0; +} + +/** + * Set pixel color + */ +//% +void setPixel(SImage_ img, int x, int y, int c) { + if (!img->inRange(x, y)) + return; + img->makeWritable(); + setCore(img, x, y, c); +} + +/** + * Get a pixel color + */ +//% +int getPixel(SImage_ img, int x, int y) { + if (!img->inRange(x, y)) + return 0; + return getCore(img, x, y); +} + +void fillRect(SImage_ img, int x, int y, int w, int h, int c); + +/** + * Fill entire image with a given color + */ +//% +void fill(SImage_ img, int c) { + if (c && img->hasPadding()) { + fillRect(img, 0, 0, img->width(), img->height(), c); + return; + } + img->makeWritable(); + memset(img->pix(), img->fillMask(c), img->pixLength()); +} + +/** + * Copy row(s) of pixel from image to buffer (8 bit per pixel). + */ +//% +void getRows(SImage_ img, int x, Buffer dst) { + if (img->bpp() != 4) + return; + + int w = img->width(); + int h = img->height(); + if (x >= w || x < 0) + return; + + uint8_t *sp = img->pix(x, 0); + uint8_t *dp = dst->data; + int n = min(dst->length, (w - x) * h) >> 1; + + while (n--) { + *dp++ = *sp & 0xf; + *dp++ = *sp >> 4; + sp++; + } +} + +/** + * Copy row(s) of pixel from buffer to image. + */ +//% +void setRows(SImage_ img, int x, Buffer src) { + if (img->bpp() != 4) + return; + + int w = img->width(); + int h = img->height(); + if (x >= w || x < 0) + return; + + img->makeWritable(); + + uint8_t *dp = img->pix(x, 0); + uint8_t *sp = src->data; + int n = min(src->length, (w - x) * h) >> 1; + + while (n--) { + *dp++ = (sp[0] & 0xf) | (sp[1] << 4); + sp += 2; + } +} + +void fillRect(SImage_ img, int x, int y, int w, int h, int c) { + if (w == 0 || h == 0 || x >= img->width() || y >= img->height()) + return; + + int x2 = x + w - 1; + int y2 = y + h - 1; + + if (x2 < 0 || y2 < 0) + return; + + img->clamp(&x2, &y2); + img->clamp(&x, &y); + w = x2 - x + 1; + h = y2 - y + 1; + + if (!img->hasPadding() && x == 0 && y == 0 && w == img->width() && h == img->height()) { + fill(img, c); + return; + } + + img->makeWritable(); + + auto bh = img->byteHeight(); + uint8_t f = img->fillMask(c); + + uint8_t *p = img->pix(x, y); + while (w-- > 0) { + if (img->bpp() == 1) { + auto ptr = p; + unsigned mask = 0x01 << (y & 7); + + for (int i = 0; i < h; ++i) { + if (mask == 0x100) { + if (h - i >= 8) { + *++ptr = f; + i += 7; + continue; + } else { + mask = 0x01; + ++ptr; + } + } + if (c) + *ptr |= mask; + else + *ptr &= ~mask; + mask <<= 1; + } + + } else if (img->bpp() == 4) { + auto ptr = p; + unsigned mask = 0x0f; + if (y & 1) + mask <<= 4; + + for (int i = 0; i < h; ++i) { + if (mask == 0xf00) { + if (h - i >= 2) { + *++ptr = f; + i++; + continue; + } else { + mask = 0x0f; + ptr++; + } + } + *ptr = (*ptr & ~mask) | (f & mask); + mask <<= 4; + } + } + p += bh; + } +} + +//% +void _fillRect(SImage_ img, int xy, int wh, int c) { + fillRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c); +} + +void mapRect(SImage_ img, int x, int y, int w, int h, Buffer map) { + if (w == 0 || h == 0 || x >= img->width() || y >= img->height()) + return; + + if (img->bpp() != 4 || map->length < 16) + return; + + int x2 = x + w - 1; + int y2 = y + h - 1; + + if (x2 < 0 || y2 < 0) + return; + + img->clamp(&x2, &y2); + img->clamp(&x, &y); + w = x2 - x + 1; + h = y2 - y + 1; + + img->makeWritable(); + + auto bh = img->byteHeight(); + auto m = map->data; + uint8_t *p = img->pix(x, y); + while (w-- > 0) { + auto ptr = p; + unsigned shift = y & 1; + for (int i = 0; i < h; i++) { + if (shift) { + *ptr = (m[*ptr >> 4] << 4) | (*ptr & 0x0f); + ptr++; + shift = 0; + } else { + *ptr = (m[*ptr & 0xf] & 0xf) | (*ptr & 0xf0); + shift = 1; + } + } + p += bh; + } +} + +//% +void _mapRect(SImage_ img, int xy, int wh, Buffer c) { + mapRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c); +} + +//% argsNullable +bool equals(SImage_ img, SImage_ other) { + if (!other) { + return false; + } + auto len = img->length(); + if (len != other->length()) { + return false; + } + return 0 == memcmp(img->data(), other->data(), len); +} + +/** + * Return a copy of the current image + */ +//% +SImage_ clone(SImage_ img) { + auto r = allocImage(img->data(), img->length()); + MEMDBG("mkImageClone: %d X %d => %p", img->width(), img->height(), r); + return r; +} + +/** + * Flips (mirrors) pixels horizontally in the current image + */ +//% +void flipX(SImage_ img) { + img->makeWritable(); + + int bh = img->byteHeight(); + auto a = img->pix(); + auto b = img->pix(img->width() - 1, 0); + + uint8_t tmp[bh]; + + while (a < b) { + memcpy(tmp, a, bh); + memcpy(a, b, bh); + memcpy(b, tmp, bh); + a += bh; + b -= bh; + } +} + +/** + * Flips (mirrors) pixels vertically in the current image + */ +//% +void flipY(SImage_ img) { + img->makeWritable(); + + // this is quite slow - for small 16x16 sprite it will take in the order of 1ms + // something faster requires quite a bit of bit tweaking, especially for mono images + for (int i = 0; i < img->width(); ++i) { + int a = 0; + int b = img->height() - 1; + while (a < b) { + int tmp = getCore(img, i, a); + setCore(img, i, a, getCore(img, i, b)); + setCore(img, i, b, tmp); + a++; + b--; + } + } +} + +/** + * Returns a transposed image (with X/Y swapped) + */ +//% +SImage_ transposed(SImage_ img) { + SImage_ r = mkImage(img->height(), img->width(), img->bpp()); + + // this is quite slow + for (int i = 0; i < img->width(); ++i) { + for (int j = 0; j < img->height(); ++i) { + setCore(r, j, i, getCore(img, i, j)); + } + } + + return r; +} + +void drawImage(SImage_ img, SImage_ from, int x, int y); + +/** + * Every pixel in image is moved by (dx,dy) + */ +//% +void scroll(SImage_ img, int dx, int dy) { + img->makeWritable(); + auto bh = img->byteHeight(); + auto w = img->width(); + if (dy != 0) { + // TODO one day we may want a more memory-efficient implementation + auto img2 = clone(img); + fill(img, 0); + drawImage(img, img2, dx, dy); + } else if (dx < 0) { + dx = -dx; + if (dx < w) + memmove(img->pix(), img->pix(dx, 0), (w - dx) * bh); + else + dx = w; + memset(img->pix(w - dx, 0), 0, dx * bh); + } else if (dx > 0) { + if (dx < w) + memmove(img->pix(dx, 0), img->pix(), (w - dx) * bh); + else + dx = w; + memset(img->pix(), 0, dx * bh); + } +} + +const uint8_t bitdouble[] = {0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, + 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff}; +const uint8_t nibdouble[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + +/** + * Stretches the image horizontally by 100% + */ +//% +SImage_ doubledX(SImage_ img) { + if (img->width() > 126) + return NULL; + + SImage_ r = mkImage(img->width() * 2, img->height(), img->bpp()); + auto src = img->pix(); + auto dst = r->pix(); + auto w = img->width(); + auto bh = img->byteHeight(); + + for (int i = 0; i < w; ++i) { + memcpy(dst, src, bh); + dst += bh; + memcpy(dst, src, bh); + dst += bh; + + src += bh; + } + + return r; +} + +/** + * Stretches the image vertically by 100% + */ +//% +SImage_ doubledY(SImage_ img) { + if (img->height() > 126) + return NULL; + + SImage_ r = mkImage(img->width(), img->height() * 2, img->bpp()); + auto src0 = img->pix(); + auto dst = r->pix(); + + auto w = img->width(); + auto sbh = img->byteHeight(); + auto bh = r->byteHeight(); + auto dbl = img->bpp() == 1 ? bitdouble : nibdouble; + + for (int i = 0; i < w; ++i) { + auto src = src0 + i * sbh; + for (int j = 0; j < bh; j += 2) { + *dst++ = dbl[*src & 0xf]; + if (j != bh - 1) + *dst++ = dbl[*src >> 4]; + src++; + } + } + + return r; +} + +/** + * Replaces one color in an image with another + */ +//% +void replace(SImage_ img, int from, int to) { + if (img->bpp() != 4) + return; + to &= 0xf; + if (from == to) + return; + + img->makeWritable(); + + // avoid bleeding 'to' color into the overflow areas of the picture + if (from == 0 && img->hasPadding()) { + for (int i = 0; i < img->height(); ++i) + for (int j = 0; j < img->width(); ++j) + if (getCore(img, j, i) == from) + setCore(img, j, i, to); + return; + } + + auto ptr = img->pix(); + auto len = img->pixLength(); + while (len--) { + auto b = *ptr; + if ((b & 0xf) == from) + b = (b & 0xf0) | to; + if ((b >> 4) == from) + b = (to << 4) | (b & 0xf); + *ptr++ = b; + } +} + +/** + * Stretches the image in both directions by 100% + */ +//% +SImage_ doubled(SImage_ img) { + SImage_ tmp = doubledX(img); + registerGCObj(tmp); + SImage_ r = doubledY(tmp); + unregisterGCObj(tmp); + return r; +} + +bool drawImageCore(SImage_ img, SImage_ from, int x, int y, int color) { + auto w = from->width(); + auto h = from->height(); + auto sh = img->height(); + auto sw = img->width(); + + if (x + w <= 0) + return false; + if (x >= sw) + return false; + if (y + h <= 0) + return false; + if (y >= sh) + return false; + + auto len = y < 0 ? min(sh, h + y) : min(sh - y, h); + auto tbp = img->bpp(); + auto fbp = from->bpp(); + auto y0 = y; + + if (color == -2 && x == 0 && y == 0 && tbp == fbp && w == sw && h == sh) { + copyFrom(img, from); + return false; + } + + // DMESG("drawIMG(%d,%d) at (%d,%d) w=%d bh=%d len=%d", + // w,h,x, y, img->width(), img->byteHeight(), len ); + + auto fromH = from->byteHeight(); + auto imgH = img->byteHeight(); + auto fromBase = from->pix(); + auto imgBase = img->pix(0, y); + +#define LOOPHD \ + for (int xx = 0; xx < w; ++xx, ++x) \ + if (0 <= x && x < sw) + + if (tbp == 4 && fbp == 4) { + auto wordH = fromH >> 2; + LOOPHD { + y = y0; + + auto fdata = (uint32_t *)fromBase + wordH * xx; + auto tdata = imgBase + imgH * x; + + // DMESG("%d,%d xx=%d/%d - %p (%p) -- %d",x,y,xx,w,tdata,img->pix(), + // (uint8_t*)fdata - from->pix()); + + auto cnt = wordH; + auto bot = min(sh, y + h); + +#define COLS(s) ((v >> (s)) & 0xf) +#define COL(s) COLS(s) + +#define STEPA(s) \ + if (COL(s) && 0 <= y && y < bot) \ + SETLOW(s); \ + y++; +#define STEPB(s) \ + if (COL(s) && 0 <= y && y < bot) \ + SETHIGH(s); \ + y++; \ + tdata++; +#define STEPAQ(s) \ + if (COL(s)) \ + SETLOW(s); +#define STEPBQ(s) \ + if (COL(s)) \ + SETHIGH(s); \ + tdata++; + +// perf: expanded version 5% faster +#define ORDER(A, B) \ + A(0); \ + B(4); \ + A(8); \ + B(12); \ + A(16); \ + B(20); \ + A(24); \ + B(28) +//#define ORDER(A,B) for (int k = 0; k < 32; k += 8) { A(k); B(4+k); } +#define LOOP(A, B, xbot) \ + while (cnt--) { \ + auto v = *fdata++; \ + if (0 <= y && y <= xbot - 8) { \ + ORDER(A##Q, B##Q); \ + y += 8; \ + } else { \ + ORDER(A, B); \ + } \ + } +#define LOOPS(xbot) \ + if (y & 1) \ + LOOP(STEPB, STEPA, xbot) \ + else \ + LOOP(STEPA, STEPB, xbot) + + if (color >= 0) { +#define SETHIGH(s) *tdata = (*tdata & 0x0f) | ((COLS(s)) << 4) +#define SETLOW(s) *tdata = (*tdata & 0xf0) | COLS(s) + LOOPS(sh) + } else if (color == -2) { +#undef COL +#define COL(s) 1 + LOOPS(bot) + } else { +#undef COL +#define COL(s) COLS(s) +#undef SETHIGH +#define SETHIGH(s) \ + if (*tdata & 0xf0) \ + return true +#undef SETLOW +#define SETLOW(s) \ + if (*tdata & 0x0f) \ + return true + LOOPS(sh) + } + } + } else if (tbp == 1 && fbp == 1) { + auto left = img->pix() - imgBase; + auto right = img->pix(0, img->height() - 1) - imgBase; + LOOPHD { + y = y0; + + auto data = fromBase + fromH * xx; + auto off = imgBase + imgH * x; + auto off0 = off + left; + auto off1 = off + right; + + int shift = (y & 7); + + int y1 = y + h + (y & 7); + int prev = 0; + + while (y < y1 - 8) { + int curr = *data++ << shift; + if (off0 <= off && off <= off1) { + uint8_t v = (curr >> 0) | (prev >> 8); + + if (color == -1) { + if (*off & v) + return true; + } else { + *off |= v; + } + } + off++; + prev = curr; + y += 8; + } + + int left = y1 - y; + if (left > 0) { + int curr = *data << shift; + if (off0 <= off && off <= off1) { + uint8_t v = ((curr >> 0) | (prev >> 8)) & (0xff >> (8 - left)); + if (color == -1) { + if (*off & v) + return true; + } else { + *off |= v; + } + } + } + } + } else if (tbp == 4 && fbp == 1) { + if (y < 0) { + fromBase = from->pix(0, -y); + imgBase = img->pix(); + } + // icon mode + LOOPHD { + auto fdata = fromBase + fromH * xx; + auto tdata = imgBase + imgH * x; + + unsigned mask = 0x01; + auto v = *fdata++; + int off = (y & 1) ? 1 : 0; + if (y < 0) { + mask <<= -y & 7; + off = 0; + } + for (int i = off; i < len + off; ++i) { + if (mask == 0x100) { + mask = 0x01; + v = *fdata++; + } + if (v & mask) { + if (i & 1) + *tdata = (*tdata & 0x0f) | (color << 4); + else + *tdata = (*tdata & 0xf0) | color; + } + mask <<= 1; + if (i & 1) + tdata++; + } + } + } + + return false; +} + +/** + * Draw given image on the current image + */ +//% +void drawImage(SImage_ img, SImage_ from, int x, int y) { + img->makeWritable(); + if (img->bpp() == 4 && from->bpp() == 4) { + drawImageCore(img, from, x, y, -2); + } else { + fillRect(img, x, y, from->width(), from->height(), 0); + drawImageCore(img, from, x, y, 0); + } +} + +/** + * Draw given image with transparent background on the current image + */ +//% +void drawTransparentImage(SImage_ img, SImage_ from, int x, int y) { + img->makeWritable(); + drawImageCore(img, from, x, y, 0); +} + +/** + * Check if the current image "collides" with another + */ +//% +bool overlapsWith(SImage_ img, SImage_ other, int x, int y) { + return drawImageCore(img, other, x, y, -1); +} + +// SImage_ format (legacy) +// byte 0: magic 0xe4 - 4 bit color; 0xe1 is monochromatic +// byte 1: width in pixels +// byte 2: height in pixels +// byte 3: padding (should be zero) +// byte 4...N: data 4 bits per pixels, high order nibble printed first, lines aligned to 32 bit +// words byte 4...N: data 1 bit per pixels, high order bit printed first, lines aligned to byte + +SImage_ convertAndWrap(Buffer buf) { + if (isValidImage(buf)) + return NEW_GC(RefImage, buf); + + // What follows in this function is mostly dead code, except if people construct image buffers + // by hand. Probably safe to remove in a year (middle of 2020) or so. When removing, also remove + // from sim. + if (!isLegacyImage(buf)) + return NULL; + + auto tmp = mkBuffer(NULL, buf->length + 4); + auto hd = (ImageHeader *)tmp->data; + auto src = buf->data; + hd->magic = IMAGE_HEADER_MAGIC; + hd->bpp = src[0] & 0xf; + hd->width = src[1]; + hd->height = src[2]; + hd->padding = 0; + memcpy(hd->pixels, src + 4, buf->length - 4); + + registerGCObj(tmp); + auto r = NEW_GC(RefImage, tmp); + unregisterGCObj(tmp); + return r; +} + +//% +void _drawIcon(SImage_ img, Buffer icon, int xy, int c) { + img->makeWritable(); + + auto iconImg = convertAndWrap(icon); + if (!iconImg || iconImg->bpp() != 1) + return; + + drawImageCore(img, iconImg, XX(xy), YY(xy), c); +} + +static void drawLineLow(SImage_ img, int x0, int y0, int x1, int y1, int c) { + int dx = x1 - x0; + int dy = y1 - y0; + int yi = 1; + if (dy < 0) { + yi = -1; + dy = -dy; + } + int D = 2 * dy - dx; + dx <<= 1; + dy <<= 1; + int y = y0; + for (int x = x0; x <= x1; ++x) { + setCore(img, x, y, c); + if (D > 0) { + y += yi; + D -= dx; + } + D += dy; + } +} + +static void drawLineHigh(SImage_ img, int x0, int y0, int x1, int y1, int c) { + int dx = x1 - x0; + int dy = y1 - y0; + int xi = 1; + if (dx < 0) { + xi = -1; + dx = -dx; + } + int D = 2 * dx - dy; + dx <<= 1; + dy <<= 1; + int x = x0; + for (int y = y0; y <= y1; ++y) { + setCore(img, x, y, c); + if (D > 0) { + x += xi; + D -= dy; + } + D += dx; + } +} + +void drawLine(SImage_ img, int x0, int y0, int x1, int y1, int c) { + if (x1 < x0) { + drawLine(img, x1, y1, x0, y0, c); + return; + } + int w = x1 - x0; + int h = y1 - y0; + + if (h == 0) { + if (w == 0) + setPixel(img, x0, y0, c); + else + fillRect(img, x0, y0, w + 1, 1, c); + return; + } + + if (w == 0) { + if (h > 0) + fillRect(img, x0, y0, 1, h + 1, c); + else + fillRect(img, x0, y1, 1, -h + 1, c); + return; + } + + if (x1 < 0 || x0 >= img->width()) + return; + if (x0 < 0) { + y0 -= (h * x0 / w); + x0 = 0; + } + if (x1 >= img->width()) { + int d = (img->width() - 1) - x1; + y1 += (h * d / w); + x1 = img->width() - 1; + } + + if (y0 < y1) { + if (y0 >= img->height() || y1 < 0) + return; + if (y0 < 0) { + x0 -= (w * y0 / h); + y0 = 0; + } + if (y1 >= img->height()) { + int d = (img->height() - 1) - y1; + x1 += (w * d / h); + y1 = img->height() - 1; + } + } else { + if (y1 >= img->height() || y0 < 0) + return; + if (y1 < 0) { + x1 -= (w * y1 / h); + y1 = 0; + } + if (y0 >= img->height()) { + int d = (img->height() - 1) - y0; + x0 += (w * d / h); + y0 = img->height() - 1; + } + } + + img->makeWritable(); + + if (h < 0) { + h = -h; + if (h < w) + drawLineLow(img, x0, y0, x1, y1, c); + else + drawLineHigh(img, x1, y1, x0, y0, c); + } else { + if (h < w) + drawLineLow(img, x0, y0, x1, y1, c); + else + drawLineHigh(img, x0, y0, x1, y1, c); + } +} + +//% +void _drawLine(SImage_ img, int xy, int wh, int c) { + drawLine(img, XX(xy), YY(xy), XX(wh), YY(wh), c); +} + +void blitRow(SImage_ img, int x, int y, SImage_ from, int fromX, int fromH) { + if (!img->inRange(x, 0) || !img->inRange(fromX, 0) || fromH <= 0) + return; + + if (img->bpp() != 4 || from->bpp() != 4) + return; + + int fy = 0; + int stepFY = (from->width() << 16) / fromH; + int endY = y + fromH; + if (endY > img->height()) + endY = img->height(); + if (y < 0) { + fy += -y * stepFY; + y = 0; + } + + auto dp = img->pix(x, y); + auto sp = from->pix(fromX, 0); + + while (y < endY) { + int p = fy >> 16, c; + if (p & 1) + c = sp[p >> 1] >> 4; + else + c = sp[p >> 1] & 0xf; + if (y & 1) { + *dp = (*dp & 0x0f) | (c << 4); + dp++; + } else { + *dp = (*dp & 0xf0) | (c & 0xf); + } + y++; + fy += stepFY; + } +} + +//% +void _blitRow(SImage_ img, int xy, SImage_ from, int xh) { + blitRow(img, XX(xy), YY(xy), from, XX(xh), YY(xh)); +} + +bool blit(SImage_ dst, SImage_ src, pxt::RefCollection *args) { + int xDst = pxt::toInt(args->getAt(0)); + int yDst = pxt::toInt(args->getAt(1)); + int wDst = pxt::toInt(args->getAt(2)); + int hDst = pxt::toInt(args->getAt(3)); + int xSrc = pxt::toInt(args->getAt(4)); + int ySrc = pxt::toInt(args->getAt(5)); + int wSrc = pxt::toInt(args->getAt(6)); + int hSrc = pxt::toInt(args->getAt(7)); + bool transparent = pxt::toBoolQuick(args->getAt(8)); + bool check = pxt::toBoolQuick(args->getAt(9)); + + int xSrcStep = (wSrc << 16) / wDst; + int ySrcStep = (hSrc << 16) / hDst; + + int xDstClip = abs(min(0, xDst)); + int yDstClip = abs(min(0, yDst)); + int xDstStart = xDst + xDstClip; + int yDstStart = yDst + yDstClip; + int xDstEnd = min(dst->width(), xDst + wDst); + int yDstEnd = min(dst->height(), yDst + hDst); + + int xSrcStart = max(0, (xSrc << 16) + xDstClip * xSrcStep); + int ySrcStart = max(0, (ySrc << 16) + yDstClip * ySrcStep); + int xSrcEnd = min(src->width(), xSrc + wSrc) << 16; + int ySrcEnd = min(src->height(), ySrc + hSrc) << 16; + + if (!check) + dst->makeWritable(); + + for (int yDstCur = yDstStart, ySrcCur = ySrcStart; yDstCur < yDstEnd && ySrcCur < ySrcEnd; ++yDstCur, ySrcCur += ySrcStep) { + int ySrcCurI = ySrcCur >> 16; + for (int xDstCur = xDstStart, xSrcCur = xSrcStart; xDstCur < xDstEnd && xSrcCur < xSrcEnd; ++xDstCur, xSrcCur += xSrcStep) { + int xSrcCurI = xSrcCur >> 16; + int cSrc = getCore(src, xSrcCurI, ySrcCurI); + if (check && cSrc) { + int cDst = getCore(dst, xDstCur, yDstCur); + if (cDst) { + return true; + } + continue; + } + if (!transparent || cSrc) { + setCore(dst, xDstCur, yDstCur, cSrc); + } + } + } + return false; +} + +//% +bool _blit(SImage_ img, SImage_ src, pxt::RefCollection *args) { + return blit(img, src, args); +} + +void fillCircle(SImage_ img, int cx, int cy, int r, int c) { + int x = r - 1; + int y = 0; + int dx = 1; + int dy = 1; + int err = dx - (r << 1); + + while (x >= y) { + fillRect(img, cx + x, cy - y, 1, 1 + (y << 1), c); + fillRect(img, cx + y, cy - x, 1, 1 + (x << 1), c); + fillRect(img, cx - x, cy - y, 1, 1 + (y << 1), c); + fillRect(img, cx - y, cy - x, 1, 1 + (x << 1), c); + if (err <= 0) { + ++y; + err += dy; + dy += 2; + } else { + --x; + dx += 2; + err += dx - (r << 1); + } + } +} + +//% +void _fillCircle(SImage_ img, int cxy, int r, int c) { + fillCircle(img, XX(cxy), YY(cxy), r, c); +} + +typedef struct +{ + int x, y; + int x0, y0; + int x1, y1; + int W,H; + int dx, dy; + int yi, xi; + int D; + int nextFuncIndex; +} LineGenState; // For keeping track of the state when generating Y values for a line, even when moving to the next X. + +typedef struct +{ + int min; + int max; +} ValueRange; + +void nextYRange_Low(int x, LineGenState *line, ValueRange *yRange) { + while (line->x == x && line->x <= line->x1 && line->x < line->W) { + if (0 <= line->x) { + if (line->y < yRange->min) yRange->min = line->y; + if (line->y > yRange->max) yRange->max = line->y; + } + if (line->D > 0) { + line->y += line->yi; + line->D -= line->dx; + } + line->D += line->dy; + ++line->x; + } +} + +void nextYRange_HighUp(int x, LineGenState *line, ValueRange *yRange) { + while (line->x == x && line->y >= line->y1 && line->x < line->W) { + if (0 <= line->x) { + if (line->y < yRange->min) yRange->min = line->y; + if (line->y > yRange->max) yRange->max = line->y; + } + if (line->D > 0) { + line->x += line->xi; + line->D += line->dy; + } + line->D += line->dx; + --line->y; + } +} +// This function is similar to the sub-function drawLineHigh for drawLine. However, it yields back after calculating all Y values of a given X. When the function is called again, it continues from the state where it yielded back previously. +void nextYRange_HighDown(int x, LineGenState *line, ValueRange *yRange) { + while (line->x == x && line->y <= line->y1 && line->x < line->W) { + if (0 <= line->x) { + if (line->y < yRange->min) yRange->min = line->y; + if (line->y > yRange->max) yRange->max = line->y; + } + if (line->D > 0) { + line->x += line->xi; + line->D -= line->dy; + } + line->D += line->dx; + ++line->y; + } +} + +LineGenState initYRangeGenerator(int16_t X0, int16_t Y0, int16_t X1, int16_t Y1) { + LineGenState line; + + line.x0 = X0, line.y0 = Y0, line.x1 = X1, line.y1 = Y1; + + line.dx = line.x1 - line.x0; + line.dy = line.y1 - line.y0; + line.y = line.y0; + line.x = line.x0; + + if ((line.dy < 0 ? -line.dy : line.dy) < line.dx) { + line.yi = 1; + if (line.dy < 0) { + line.yi = -1; + line.dy = -line.dy; + } + line.D = 2 * line.dy - line.dx; + line.dx <<= 1; + line.dy <<= 1; + + line.nextFuncIndex = 0; + return line; + } else { + line.xi = 1; + // if (dx < 0) {//should not hit + // PANIC(); + // } + if (line.dy < 0) { + line.D = 2 * line.dx + line.dy; + line.dx <<= 1; + line.dy <<= 1; + + line.nextFuncIndex = 1; + return line; + } else { + line.D = 2 * line.dx - line.dy; + line.dx <<= 1; + line.dy <<= 1; + + line.nextFuncIndex = 2; + return line; + } + } +} + +// core of draw vertical line for repeatly calling, eg.: fillTriangle() or fillPolygon4() +// value range/safety check not included +// prepare "img->makeWritable();" and "uint8_t f = img->fillMask(c);" outside required. +// bpp=4 support only right now +void drawVLineCore(SImage_ img, int x, int y, int h, uint8_t f) { + uint8_t *p = img->pix(x, y); + auto ptr = p; + unsigned mask = 0x0f; + if (y & 1) + mask <<= 4; + for (int i = 0; i < h; ++i) { + if (mask == 0xf00) { + if (h - i >= 2) { + *++ptr = f; + i++; + continue; + } else { + mask = 0x0f; + ptr++; + } + } + *ptr = (*ptr & ~mask) | (f & mask); + mask <<= 4; + } +} + +void drawVLine(SImage_ img, int x, int y, int h, int c) { + int H = height(img); + uint8_t f = img->fillMask(c); + if (x < 0 || x >= width(img) || y >= H || y + h - 1 < 0) + return; + if (y < 0){ + h += y; + y = 0; + } + if (y + h > H) + h = H - y; + drawVLineCore(img, x, y, h, f); +} + +void fillTriangle(SImage_ img, int x0, int y0, int x1, int y1, int x2, int y2, int c) { + if (x1 < x0) { + pxt::swap(x0, x1); + pxt::swap(y0, y1); + } + if (x2 < x1) { + pxt::swap(x1, x2); + pxt::swap(y1, y2); + } + if (x1 < x0) { + pxt::swap(x0, x1); + pxt::swap(y0, y1); + } + + LineGenState lines[] = { + initYRangeGenerator(x0, y0, x2, y2), + initYRangeGenerator(x0, y0, x1, y1), + initYRangeGenerator(x1, y1, x2, y2) + }; + + int W = width(img), H = height(img); + lines[0].W = lines[1].W = lines[2].W = W; + lines[0].H = lines[1].H = lines[2].H = H; + + // We have 3 different sub-functions to generate Ys of edges, each particular edge maps to one of them. + // Use function pointers to avoid judging which function to call at every X. + typedef void (*FP_NEXT)(int x, LineGenState *line, ValueRange *yRange); + FP_NEXT nextFuncList[] = { nextYRange_Low, nextYRange_HighUp, nextYRange_HighDown }; + FP_NEXT fpNext0 = nextFuncList[lines[0].nextFuncIndex]; + FP_NEXT fpNext1 = nextFuncList[lines[1].nextFuncIndex]; + FP_NEXT fpNext2 = nextFuncList[lines[2].nextFuncIndex]; + + ValueRange yRange = {H, -1}; + img->makeWritable(); + uint8_t f = img->fillMask(c); + + for (int x = lines[1].x0; x <= min(x1, W - 1); x++) { + yRange.min = H; + yRange.max = -1; + fpNext0(x, &lines[0], &yRange); + fpNext1(x, &lines[1], &yRange); + + if (x < 0 || yRange.min >= H || yRange.max < 0) + continue; + if (yRange.min < 0) + yRange.min = 0; + if (yRange.max >= H) + yRange.max = H - 1; + drawVLineCore(img, x, yRange.min, yRange.max - yRange.min + 1, f); + } + + fpNext2(lines[2].x0, &lines[2], &yRange); + + for (int x = lines[2].x0 + 1; x <= min(x2, W - 1); x++) { + yRange.min = H; + yRange.max = -1; + fpNext0(x, &lines[0], &yRange); + fpNext2(x, &lines[2], &yRange); + + if (x < 0 || yRange.min >= H || yRange.max < 0) + continue; + if (yRange.min < 0) + yRange.min = 0; + if (yRange.max >= H) + yRange.max = H - 1; + drawVLineCore(img, x, yRange.min, yRange.max - yRange.min + 1, f); + } +} + +void fillPolygon4(SImage_ img, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, int c) { + LineGenState lines[] = { + (x0 < x1) ? initYRangeGenerator(x0, y0, x1, y1) : initYRangeGenerator(x1, y1, x0, y0), + (x1 < x2) ? initYRangeGenerator(x1, y1, x2, y2) : initYRangeGenerator(x2, y2, x1, y1), + (x2 < x3) ? initYRangeGenerator(x2, y2, x3, y3) : initYRangeGenerator(x3, y3, x2, y2), + (x0 < x3) ? initYRangeGenerator(x0, y0, x3, y3) : initYRangeGenerator(x3, y3, x0, y0)}; + + int W = width(img), H = height(img); + lines[0].W = lines[1].W = lines[2].W = lines[3].W = W; + lines[0].H = lines[1].H = lines[2].H = lines[3].H = H; + + int minX = min(min(x0, x1), min(x2, x3)); + int maxX = min(max(max(x0, x1), max(x2, x3)), W - 1); + + typedef void (*FP_NEXT)(int x, LineGenState *line, ValueRange *yRange); + FP_NEXT nextFuncList[] = { nextYRange_Low, nextYRange_HighUp, nextYRange_HighDown }; + FP_NEXT fpNext0 = nextFuncList[lines[0].nextFuncIndex]; + FP_NEXT fpNext1 = nextFuncList[lines[1].nextFuncIndex]; + FP_NEXT fpNext2 = nextFuncList[lines[2].nextFuncIndex]; + FP_NEXT fpNext3 = nextFuncList[lines[3].nextFuncIndex]; + + ValueRange yRange = { H, -1 }; + img->makeWritable(); + uint8_t f = img->fillMask(c); + + for (int x = minX; x <= maxX; x++) { + yRange.min = H; + yRange.max = -1; + fpNext0(x, &lines[0], &yRange); + fpNext1(x, &lines[1], &yRange); + fpNext2(x, &lines[2], &yRange); + fpNext3(x, &lines[3], &yRange); + + if (x < 0 || yRange.min >= H || yRange.max < 0) + continue; + if (yRange.min < 0) + yRange.min = 0; + if (yRange.max >= H) + yRange.max = H - 1; + drawVLineCore(img, x, yRange.min, yRange.max - yRange.min + 1, f); + } +} + +//% +void _fillTriangle(SImage_ img, pxt::RefCollection *args) { + fillTriangle( + img, + pxt::toInt(args->getAt(0)), + pxt::toInt(args->getAt(1)), + pxt::toInt(args->getAt(2)), + pxt::toInt(args->getAt(3)), + pxt::toInt(args->getAt(4)), + pxt::toInt(args->getAt(5)), + pxt::toInt(args->getAt(6)) + ); +} + +// This polygon fill is similar to fillTriangle(): Scan minY and maxY of all edges at each X, and draw a vertical line between (x,minY)~(x,maxY). +// The main difference is that it sorts the endpoints of each edge, x0 < x1, to draw from left to right, but doesn't sort the edges as it's too time consuming. +// Instead, just call next(), which returns immediately if the x is not in range of the edge in horizon. +// NOTE: Unlike triangles, edges of a polygon can cross a vertical line at a given X multi time. This algorithm can fill correctly only if edges meet this condition: Any vertical line(x) cross edges at most 2 times. +// Fortunately, no matter what perspective transform is applied, a rectangle/trapezoid will still meet this condition. +// Ref: https://forum.makecode.com/t/new-3d-engine-help-filling-4-sided-polygons/18641/9 +//% +void _fillPolygon4(SImage_ img, pxt::RefCollection *args) { + fillPolygon4( + img, + pxt::toInt(args->getAt(0)), + pxt::toInt(args->getAt(1)), + pxt::toInt(args->getAt(2)), + pxt::toInt(args->getAt(3)), + pxt::toInt(args->getAt(4)), + pxt::toInt(args->getAt(5)), + pxt::toInt(args->getAt(6)), + pxt::toInt(args->getAt(7)), + pxt::toInt(args->getAt(8)) + ); +} + +} // namespace SImageMethods + +namespace simage { +/** + * Create new empty (transparent) image + */ +//% +SImage_ create(int width, int height) { + SImage_ r = mkImage(width, height, IMAGE_BITS); + if (r) + memset(r->pix(), 0, r->pixLength()); + else + target_panic(PANIC_INVALID_IMAGE); + return r; +} + +/** + * Create new image with given content + */ +//% +SImage_ ofBuffer(Buffer buf) { + return SImageMethods::convertAndWrap(buf); +} + +/** + * Double the size of an icon + */ +//% +Buffer doubledIcon(Buffer icon) { + if (!isValidImage(icon)) + return NULL; + + auto r = NEW_GC(RefImage, icon); + registerGCObj(r); + auto t = SImageMethods::doubled(r); + unregisterGCObj(r); + return t->buffer; +} + +} // namespace image + +// This is 6.5x faster than standard on word-aligned copy +// probably should move to codal + +#ifndef __linux__ +extern "C" void *memcpy(void *dst, const void *src, size_t sz) { + void *dst0 = dst; + if (sz >= 4 && !((uintptr_t)dst & 3) && !((uintptr_t)src & 3)) { + size_t cnt = sz >> 2; + uint32_t *d = (uint32_t *)dst; + const uint32_t *s = (const uint32_t *)src; + while (cnt--) { + *d++ = *s++; + } + sz &= 3; + dst = d; + src = s; + } + + // see comment in memset() below (have not seen optimization here, but better safe than sorry) + volatile uint8_t *dd = (uint8_t *)dst; + volatile uint8_t *ss = (uint8_t *)src; + + while (sz--) { + *dd++ = *ss++; + } + + return dst0; +} + +extern "C" void *memset(void *dst, int v, size_t sz) { + void *dst0 = dst; + if (sz >= 4 && !((uintptr_t)dst & 3)) { + size_t cnt = sz >> 2; + uint32_t vv = 0x01010101 * v; + uint32_t *d = (uint32_t *)dst; + while (cnt--) { + *d++ = vv; + } + sz &= 3; + dst = d; + } + + // without volatile here, GCC may optimize the loop to memset() call which is obviously not great + volatile uint8_t *dd = (uint8_t *)dst; + + while (sz--) { + *dd++ = v; + } + + return dst0; +} +#endif diff --git a/libs/screen/image.d.ts b/libs/screen/image.d.ts new file mode 100644 index 00000000000..32c64021ecb --- /dev/null +++ b/libs/screen/image.d.ts @@ -0,0 +1,121 @@ +//% fixedInstances decompileIndirectFixedInstances +interface SImage { + /** + * Fill a rectangle + */ + //% helper=imageFillRect blockNamespace="images" inlineInputMode="inline" group="Drawing" + //% block="fill rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker" + //% help=images/image/fill-rect + fillRect(x: number, y: number, w: number, h: number, c: color): void; + + /** + * Replace colors in a rectangle + */ + //% helper=imageMapRect + mapRect(x: number, y: number, w: number, h: number, colorMap: Buffer): void; + + /** + * Draw a line + */ + //% helper=imageDrawLine blockNamespace="images" inlineInputMode="inline" group="Drawing" + //% block="draw line in %picture=variables_get from x %x0 y %y0 to x %x1 y %y1 %c=colorindexpicker" + //% help=images/image/draw-line + drawLine(x0: number, y0: number, x1: number, y1: number, c: color): void; + + /** + * Draw an empty rectangle + */ + //% helper=imageDrawRect blockNamespace="images" inlineInputMode="inline" group="Drawing" + //% block="draw rectangle in %picture=variables_get at x %x y %y width %w height %h %c=colorindexpicker" + //% help=images/image/draw-rect + drawRect(x: number, y: number, w: number, h: number, c: color): void; + + /** + * Set pixel color + */ + //% shim=SImageMethods::setPixel blockNamespace="images" group="Drawing" + //% block="set %picture=variables_get color at x %x y %y to %c=colorindexpicker" + //% help=images/image/set-pixel + setPixel(x: int32, y: int32, c: int32): void; + + /** + * Get a pixel color + */ + //% shim=SImageMethods::getPixel blockNamespace="images" group="Drawing" + //% block="%picture=variables_get color at x %x y %y" + //% help=images/image/get-pixel + getPixel(x: int32, y: int32): int32; + + /** + * Fill entire image with a given color + */ + //% shim=SImageMethods::fill blockNamespace="images" group="Drawing" + //% block="fill %picture=variables_get with %c=colorindexpicker" + //% help=images/image/fill + fill(c: int32): void; + + /** + * Return a copy of the current image + */ + //% shim=SImageMethods::clone blockNamespace="images" group="Create" + //% block="clone %picture=variables_get" + //% help=images/image/clone + clone(): SImage; + + /** + * Flips (mirrors) pixels horizontally in the current image + */ + //% shim=SImageMethods::flipX blockNamespace="images" group="Transformations" + //% block="flip %picture=variables_get horizontally" + //% help=images/image/flip-x + flipX(): void; + + /** + * Flips (mirrors) pixels vertically in the current image + */ + //% shim=SImageMethods::flipY blockNamespace="images" group="Transformations" + //% block="flip %picture=variables_get vertically" + //% help=images/image/flip-y + flipY(): void; + + /** + * Every pixel in image is moved by (dx,dy) + */ + //% shim=SImageMethods::scroll blockNamespace="images" group="Transformations" + //% help=images/image/scroll + scroll(dx: int32, dy: int32): void; + + /** + * Replaces one color in an image with another + */ + //% shim=SImageMethods::replace blockNamespace="images" group="Transformations" + //% block="change color in %picture=variables_get from %from=colorindexpicker to %to=colorindexpicker" + //% help=images/image/replace + replace(from: int32, to: int32): void; + + /** + * Returns true if the provided image is the same as this image, + * otherwise returns false. + */ + //% shim=SImageMethods::equals + //% blockNamespace="images" group="Compare" + //% block="$this is equal to image $other" + //% this.shadow=variables_get + //% this.defl="picture" + //% other.shadow=screen_image_picker + //% help=images/image/equals + equals(other: SImage): boolean; + + //% shim=SImageMethods::isStatic + isStatic(): boolean; + + //% shim=SImageMethods::revision + revision(): number; +} + +declare namespace simage { + //% blockNamespace="images" + //% block="create image width %width height %height" group="Create" + //% help=images/create + function create(width: number, height: number): SImage; +} \ No newline at end of file diff --git a/libs/screen/image.ts b/libs/screen/image.ts new file mode 100644 index 00000000000..6d7092471b8 --- /dev/null +++ b/libs/screen/image.ts @@ -0,0 +1,292 @@ +type color = number + +namespace simage { + export function repeatY(count: number, image: SImage) { + let arr = [image] + while (--count > 0) + arr.push(image) + return concatY(arr) + } + + export function concatY(images: SImage[]) { + let w = 0 + let h = 0 + for (let img of images) { + w = Math.max(img.width, w) + h += img.height + } + let r = simage.create(w, h) + let y = 0 + for (let img of images) { + let x = (w - img.width) >> 1 + r.drawImage(img, x, y) + y += img.height + } + return r + } +} + + +//% snippet='img` `' +//% pySnippet='img(""" """)' +//% fixedInstances +interface SImage { + /** + * Draw an icon (monochromatic image) using given color + */ + //% helper=imageDrawIcon + drawIcon(icon: Buffer, x: number, y: number, c: color): void; + + /** + * Fill a rectangle + */ + //% helper=imageFillRect + fillRect(x: number, y: number, w: number, h: number, c: color): void; + + /** + * Draw a line + */ + //% helper=imageDrawLine + drawLine(x0: number, y0: number, x1: number, y1: number, c: color): void; + + /** + * Draw an empty rectangle + */ + //% helper=imageDrawRect + drawRect(x: number, y: number, w: number, h: number, c: color): void; + + /** + * Draw a circle + */ + //% helper=imageDrawCircle + drawCircle(cx: number, cy: number, r: number, c: color): void; + + /** + * Fills a circle + */ + //% helper=imageFillCircle + fillCircle(cx: number, cy: number, r: number, c: color): void; + + /** + * Fills a triangle + */ + //% helper=imageFillTriangle + fillTriangle(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, col: number): void; + + /** + * Fills a 4-side-polygon + */ + //% helper=imageFillPolygon4 + fillPolygon4(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, col: number): void; + + /** + * Returns an image rotated by -90, 0, 90, 180, 270 deg clockwise + */ + //% helper=imageRotated + rotated(deg: number): SImage; + + /** + * Scale and copy a row of pixels from a texture. + */ + //% helper=imageBlitRow + blitRow(dstX: number, dstY: number, from: SImage, fromX: number, fromH: number): void; + + /** + * Copy an image from a source rectangle to a destination rectangle, stretching or + * compressing to fit the dimensions of the destination rectangle, if necessary. + */ + //% helper=imageBlit + blit(xDst: number, yDst: number, wDst: number, hDst: number, src: SImage, xSrc: number, ySrc: number, wSrc: number, hSrc: number, transparent: boolean, check: boolean): boolean; +} + +interface ScreenImage extends SImage { + /** + * Sets the screen backlight brightness (10-100) + */ + //% helper=setScreenBrightness + setBrightness(deg: number): SImage; + + /** + * Gets current screen backlight brightness (0-100) + */ + //% helper=screenBrightness + brightness(): number; +} + +// pxt compiler currently crashes on non-functions in helpers namespace; will fix +namespace _helpers_workaround { + export let brightness = 100 +} + +namespace helpers { + //% shim=SImageMethods::_drawLine + function _drawLine(img: SImage, xy: number, wh: number, c: color): void { } + + //% shim=SImageMethods::_fillRect + function _fillRect(img: SImage, xy: number, wh: number, c: color): void { } + + //% shim=SImageMethods::_mapRect + function _mapRect(img: SImage, xy: number, wh: number, m: Buffer): void { } + + //% shim=SImageMethods::_drawIcon + function _drawIcon(img: SImage, icon: Buffer, xy: number, c: color): void { } + + //% shim=SImageMethods::_fillCircle + declare function _fillCircle(img: SImage, cxy: number, r: number, c: color): void; + + //% shim=SImageMethods::_blitRow + declare function _blitRow(img: SImage, xy: number, from: SImage, xh: number): void; + + //% shim=SImageMethods::_blit + declare function _blit(img: SImage, src: SImage, args: number[]): boolean; + + //% shim=SImageMethods::_fillTriangle + declare function _fillTriangle(img: SImage, args: number[]): void; + + //% shim=SImageMethods::_fillPolygon4 + declare function _fillPolygon4(img: SImage, args: number[]): void; + + function pack(x: number, y: number) { + return (Math.clamp(-30000, 30000, x | 0) & 0xffff) | (Math.clamp(-30000, 30000, y | 0) << 16) + } + + let _blitArgs: number[]; + + export function imageBlit(img: SImage, xDst: number, yDst: number, wDst: number, hDst: number, src: SImage, xSrc: number, ySrc: number, wSrc: number, hSrc: number, transparent: boolean, check: boolean): boolean { + _blitArgs = _blitArgs || []; + _blitArgs[0] = xDst | 0; + _blitArgs[1] = yDst | 0; + _blitArgs[2] = wDst | 0; + _blitArgs[3] = hDst | 0; + _blitArgs[4] = xSrc | 0; + _blitArgs[5] = ySrc | 0; + _blitArgs[6] = wSrc | 0; + _blitArgs[7] = hSrc | 0; + _blitArgs[8] = transparent ? 1 : 0; + _blitArgs[9] = check ? 1 : 0; + return _blit(img, src, _blitArgs); + } + + export function imageBlitRow(img: SImage, dstX: number, dstY: number, from: SImage, fromX: number, fromH: number): void { + _blitRow(img, pack(dstX, dstY), from, pack(fromX, fromH)) + } + + export function imageDrawIcon(img: SImage, icon: Buffer, x: number, y: number, c: color): void { + _drawIcon(img, icon, pack(x, y), c) + } + export function imageFillRect(img: SImage, x: number, y: number, w: number, h: number, c: color): void { + _fillRect(img, pack(x, y), pack(w, h), c) + } + export function imageMapRect(img: SImage, x: number, y: number, w: number, h: number, m: Buffer): void { + _mapRect(img, pack(x, y), pack(w, h), m) + } + export function imageDrawLine(img: SImage, x: number, y: number, w: number, h: number, c: color): void { + _drawLine(img, pack(x, y), pack(w, h), c) + } + export function imageDrawRect(img: SImage, x: number, y: number, w: number, h: number, c: color): void { + if (w == 0 || h == 0) return + w-- + h-- + imageDrawLine(img, x, y, x + w, y, c) + imageDrawLine(img, x, y, x, y + h, c) + imageDrawLine(img, x + w, y + h, x + w, y, c) + imageDrawLine(img, x + w, y + h, x, y + h, c) + } + + export function imageDrawCircle(img: SImage, cx: number, cy: number, r: number, col: number) { + cx = cx | 0; + cy = cy | 0; + r = r | 0; + // short cuts + if (r < 0) + return; + + // Bresenham's algorithm + let x = 0 + let y = r + let d = 3 - 2 * r + + while (y >= x) { + img.setPixel(cx + x, cy + y, col) + img.setPixel(cx - x, cy + y, col) + img.setPixel(cx + x, cy - y, col) + img.setPixel(cx - x, cy - y, col) + img.setPixel(cx + y, cy + x, col) + img.setPixel(cx - y, cy + x, col) + img.setPixel(cx + y, cy - x, col) + img.setPixel(cx - y, cy - x, col) + x++ + if (d > 0) { + y-- + d += 4 * (x - y) + 10 + } else { + d += 4 * x + 6 + } + } + } + + export function imageFillCircle(img: SImage, cx: number, cy: number, r: number, col: number) { + _fillCircle(img, pack(cx, cy), r, col); + } + + export function imageFillTriangle(img: SImage, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, col: number) { + _blitArgs = _blitArgs || []; + _blitArgs[0] = x0; + _blitArgs[1] = y0; + _blitArgs[2] = x1; + _blitArgs[3] = y1; + _blitArgs[4] = x2; + _blitArgs[5] = y2; + _blitArgs[6] = col; + _fillTriangle(img, _blitArgs); + } + + export function imageFillPolygon4(img: SImage, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, col: number) { + _blitArgs = _blitArgs || []; + _blitArgs[0] = x0; + _blitArgs[1] = y0; + _blitArgs[2] = x1; + _blitArgs[3] = y1; + _blitArgs[4] = x2; + _blitArgs[5] = y2; + _blitArgs[6] = x3; + _blitArgs[7] = y3; + _blitArgs[8] = col; + _fillPolygon4(img, _blitArgs); + } + + /** + * Returns an image rotated by 90, 180, 270 deg clockwise + */ + export function imageRotated(img: SImage, deg: number) { + if (deg == -90 || deg == 270) { + let r = img.transposed(); + r.flipY(); + return r; + } else if (deg == 180 || deg == -180) { + let r = img.clone(); + r.flipX(); + r.flipY(); + return r; + } else if (deg == 90) { + let r = img.transposed(); + r.flipX(); + return r; + } else { + return null; + } + } + + //% shim=pxt::setScreenBrightness + function _setScreenBrightness(brightness: number) { } + + export function setScreenBrightness(img: SImage, b: number) { + b = Math.clamp(10, 100, b | 0); + _helpers_workaround.brightness = b + _setScreenBrightness(_helpers_workaround.brightness) + } + + export function screenBrightness(img: SImage) { + return _helpers_workaround.brightness + } +} diff --git a/libs/screen/imagesoverrides.jres b/libs/screen/imagesoverrides.jres new file mode 100644 index 00000000000..077404aaa41 --- /dev/null +++ b/libs/screen/imagesoverrides.jres @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/libs/screen/imagesoverrides.ts b/libs/screen/imagesoverrides.ts new file mode 100644 index 00000000000..89030b481b1 --- /dev/null +++ b/libs/screen/imagesoverrides.ts @@ -0,0 +1 @@ +// replace with built-in images \ No newline at end of file diff --git a/libs/screen/ns.ts b/libs/screen/ns.ts new file mode 100644 index 00000000000..0519ecba6ea --- /dev/null +++ b/libs/screen/ns.ts @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/libs/screen/panic.cpp b/libs/screen/panic.cpp new file mode 100644 index 00000000000..f93e2ffee9a --- /dev/null +++ b/libs/screen/panic.cpp @@ -0,0 +1 @@ +// potentially overriden in targets \ No newline at end of file diff --git a/libs/screen/pxt.json b/libs/screen/pxt.json new file mode 100644 index 00000000000..d10e600bebe --- /dev/null +++ b/libs/screen/pxt.json @@ -0,0 +1,26 @@ +{ + "name": "screen", + "description": "The screen library", + "files": [ + "screen.cpp", + "panic.cpp", + "image.cpp", + "image.ts", + "screenimage.ts", + "text.ts", + "frame.ts", + "shims.d.ts", + "fieldeditors.ts", + "targetoverrides.ts", + "ns.ts", + "image.d.ts", + "pxtparts.json", + "imagesoverrides.jres", + "imagesoverrides.ts", + "font12.jres" + ], + "public": true, + "dependencies": { + "core": "file:../core" + } +} diff --git a/libs/screen/pxtparts.json b/libs/screen/pxtparts.json new file mode 100644 index 00000000000..07ccc7f0278 --- /dev/null +++ b/libs/screen/pxtparts.json @@ -0,0 +1,115 @@ +{ + "screen": { + "simulationBehavior": "screen", + "visual": { + "builtIn": "screen", + "width": 158.43856811523438, + "height": 146.8025665283203, + "pinDistance": 14.91, + "pinLocations": [ + { + "x": 4.227952701380444, + "y": 3.1650031792503945 + }, + { + "x": 18.170226805137037, + "y": 3.1650031792503945 + }, + { + "x": 46.05478386015504, + "y": 3.1650031792503945 + }, + { + "x": 59.99706238766404, + "y": 3.1650031792503945 + }, + { + "x": 73.93934976267785, + "y": 3.1650031792503945 + }, + { + "x": 87.88161944268204, + "y": 3.1650031792503945 + }, + { + "x": 101.82389797019104, + "y": 3.1650031792503945 + }, + { + "x": 32.11250533264604, + "y": 3.1650031792503945 + }, + { + "x": 117.68761950246274, + "y": 3.1650031792503945 + } + ] + }, + "numberOfPins": 9, + "instantiation": { + "kind": "singleton" + }, + "pinDefinitions": [ + { + "target": "ground", + "style": "male", + "orientation": "-Z" + }, + { + "target": "threeVolt", + "style": "male", + "orientation": "-Z" + }, + { + "target": "DISPLAY_DC", + "style": "male", + "orientation": "-Z" + }, + { + "target": "DISPLAY_CS", + "style": "male", + "orientation": "-Z" + }, + { + "target": "DISPLAY_MOSI", + "style": "male", + "orientation": "-Z" + }, + { + "target": "DISPLAY_SCK", + "style": "male", + "orientation": "-Z" + }, + { + "target": "DISPLAY_MISO", + "style": "male", + "orientation": "-Z" + }, + { + "target": "DISPLAY_RST", + "style": "male", + "orientation": "-Z" + }, + { + "target": "threeVolt", + "style": "male", + "orientation": "-Z" + } + ], + "assembly": [ + { + "pinIndices": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ] + } + ] + } +} \ No newline at end of file diff --git a/libs/screen/screenimage.ts b/libs/screen/screenimage.ts new file mode 100644 index 00000000000..71eecb82b22 --- /dev/null +++ b/libs/screen/screenimage.ts @@ -0,0 +1,12 @@ + +namespace simage { + /** + * Get the screen image + */ + //% blockNamespace="images" group="Create" + //% blockId=imagescreen block="screen" + //% help=images/screen-image + export function screenImage(): SImage { + return screen; + } +} diff --git a/libs/screen/shims.d.ts b/libs/screen/shims.d.ts new file mode 100644 index 00000000000..b2a2aceeff9 --- /dev/null +++ b/libs/screen/shims.d.ts @@ -0,0 +1,153 @@ +// Auto-generated. Do not edit. + + +declare interface SImage { + /** + * Get the width of the image + */ + //% property shim=SImageMethods::width + width: int32; + + /** + * Get the height of the image + */ + //% property shim=SImageMethods::height + height: int32; + + /** + * True if the image is monochromatic (black and white) + */ + //% property shim=SImageMethods::isMono + isMono: boolean; + + /** + * Sets all pixels in the current image from the other image, which has to be of the same size and + * bpp. + */ + //% shim=SImageMethods::copyFrom + copyFrom(from: SImage): void; + + /** + * Set pixel color + */ + //% shim=SImageMethods::setPixel + setPixel(x: int32, y: int32, c: int32): void; + + /** + * Get a pixel color + */ + //% shim=SImageMethods::getPixel + getPixel(x: int32, y: int32): int32; + + /** + * Fill entire image with a given color + */ + //% shim=SImageMethods::fill + fill(c: int32): void; + + /** + * Copy row(s) of pixel from image to buffer (8 bit per pixel). + */ + //% shim=SImageMethods::getRows + getRows(x: int32, dst: Buffer): void; + + /** + * Copy row(s) of pixel from buffer to image. + */ + //% shim=SImageMethods::setRows + setRows(x: int32, src: Buffer): void; + + /** + * Return a copy of the current image + */ + //% shim=SImageMethods::clone + clone(): SImage; + + /** + * Flips (mirrors) pixels horizontally in the current image + */ + //% shim=SImageMethods::flipX + flipX(): void; + + /** + * Flips (mirrors) pixels vertically in the current image + */ + //% shim=SImageMethods::flipY + flipY(): void; + + /** + * Returns a transposed image (with X/Y swapped) + */ + //% shim=SImageMethods::transposed + transposed(): SImage; + + /** + * Every pixel in image is moved by (dx,dy) + */ + //% shim=SImageMethods::scroll + scroll(dx: int32, dy: int32): void; + + /** + * Stretches the image horizontally by 100% + */ + //% shim=SImageMethods::doubledX + doubledX(): SImage; + + /** + * Stretches the image vertically by 100% + */ + //% shim=SImageMethods::doubledY + doubledY(): SImage; + + /** + * Replaces one color in an image with another + */ + //% shim=SImageMethods::replace + replace(from: int32, to: int32): void; + + /** + * Stretches the image in both directions by 100% + */ + //% shim=SImageMethods::doubled + doubled(): SImage; + + /** + * Draw given image on the current image + */ + //% shim=SImageMethods::drawImage + drawImage(from: SImage, x: int32, y: int32): void; + + /** + * Draw given image with transparent background on the current image + */ + //% shim=SImageMethods::drawTransparentImage + drawTransparentImage(from: SImage, x: int32, y: int32): void; + + /** + * Check if the current image "collides" with another + */ + //% shim=SImageMethods::overlapsWith + overlapsWith(other: SImage, x: int32, y: int32): boolean; +} +declare namespace image { + + /** + * Create new empty (transparent) image + */ + //% shim=image::create + function create(width: int32, height: int32): SImage; + + /** + * Create new image with given content + */ + //% shim=image::ofBuffer + function ofBuffer(buf: Buffer): SImage; + + /** + * Double the size of an icon + */ + //% shim=image::doubledIcon + function doubledIcon(icon: Buffer): Buffer; +} + +// Auto-generated. Do not edit. Really. diff --git a/libs/screen/sim/image.ts b/libs/screen/sim/image.ts new file mode 100644 index 00000000000..17039c8efa9 --- /dev/null +++ b/libs/screen/sim/image.ts @@ -0,0 +1,1141 @@ +namespace pxsim { + export class RefImage extends RefObject { + _width: number; + _height: number; + _bpp: number; + data: Uint8Array; + isStatic = true; + revision: number; + + constructor(w: number, h: number, bpp: number) { + super(); + this.revision = 0; + this.data = new Uint8Array(w * h) + this._width = w + this._height = h + this._bpp = bpp + } + + scan(mark: (path: string, v: any) => void) { } + gcKey() { return "SImage" } + gcSize() { return 4 + (this.data.length + 3 >> 3) } + gcIsStatic() { return this.isStatic } + + pix(x: number, y: number) { + return (x | 0) + (y | 0) * this._width + } + + inRange(x: number, y: number) { + return 0 <= (x | 0) && (x | 0) < this._width && + 0 <= (y | 0) && (y | 0) < this._height; + } + + color(c: number): number { + return c & 0xff + } + + clamp(x: number, y: number) { + x |= 0 + y |= 0 + + if (x < 0) x = 0 + else if (x >= this._width) + x = this._width - 1 + + if (y < 0) y = 0 + else if (y >= this._height) + y = this._height - 1 + + return [x, y] + } + + makeWritable() { + this.revision++; + this.isStatic = false + } + + toDebugString() { + return this._width + "x" + this._height + } + } +} + +namespace pxsim.SImageMethods { + export function XX(x: number) { return (x << 16) >> 16 } + export function YY(x: number) { return x >> 16 } + + export function width(img: RefImage) { return img._width } + + export function height(img: RefImage) { return img._height } + + export function isMono(img: RefImage) { return img._bpp == 1 } + + export function isStatic(img: RefImage) { return img.gcIsStatic() } + + export function revision(img: RefImage) { return img.revision } + + export function setPixel(img: RefImage, x: number, y: number, c: number) { + img.makeWritable() + if (img.inRange(x, y)) + img.data[img.pix(x, y)] = img.color(c) + } + + export function getPixel(img: RefImage, x: number, y: number) { + if (img.inRange(x, y)) + return img.data[img.pix(x, y)] + return 0 + } + + export function fill(img: RefImage, c: number) { + img.makeWritable() + img.data.fill(img.color(c)) + } + + export function fillRect(img: RefImage, x: number, y: number, w: number, h: number, c: number) { + if (w == 0 || h == 0 || x >= img._width || y >= img._height || x + w - 1 < 0 || y + h - 1 < 0) + return; + img.makeWritable() + let [x2, y2] = img.clamp(x + w - 1, y + h - 1); + [x, y] = img.clamp(x, y) + let p = img.pix(x, y) + w = x2 - x + 1 + h = y2 - y + 1 + let d = img._width - w + c = img.color(c) + while (h-- > 0) { + for (let i = 0; i < w; ++i) + img.data[p++] = c + p += d + } + } + + export function _fillRect(img: RefImage, xy: number, wh: number, c: number) { + fillRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c) + } + + export function mapRect(img: RefImage, x: number, y: number, w: number, h: number, c: RefBuffer) { + if (c.data.length < 16) + return + img.makeWritable() + let [x2, y2] = img.clamp(x + w - 1, y + h - 1); + [x, y] = img.clamp(x, y) + let p = img.pix(x, y) + w = x2 - x + 1 + h = y2 - y + 1 + let d = img._width - w + + while (h-- > 0) { + for (let i = 0; i < w; ++i) { + img.data[p] = c.data[img.data[p]] + p++ + } + p += d + } + } + + export function _mapRect(img: RefImage, xy: number, wh: number, c: RefBuffer) { + mapRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c) + } + + export function equals(img: RefImage, other: RefImage) { + if (!other || img._bpp != other._bpp || img._width != other._width || img._height != other._height) { + return false; + } + let imgData = img.data; + let otherData = other.data; + let len = imgData.length; + for (let i = 0; i < len; i++) { + if (imgData[i] != otherData[i]) { + return false; + } + } + return true; + } + + export function getRows(img: RefImage, x: number, dst: RefBuffer) { + x |= 0 + if (!img.inRange(x, 0)) + return + + let dp = 0 + let len = Math.min(dst.data.length, (img._width - x) * img._height) + let sp = x + let hh = 0 + while (len--) { + if (hh++ >= img._height) { + hh = 1 + sp = ++x + } + dst.data[dp++] = img.data[sp] + sp += img._width + } + } + + export function setRows(img: RefImage, x: number, src: RefBuffer) { + x |= 0 + if (!img.inRange(x, 0)) + return + + let sp = 0 + let len = Math.min(src.data.length, (img._width - x) * img._height) + let dp = x + let hh = 0 + while (len--) { + if (hh++ >= img._height) { + hh = 1 + dp = ++x + } + img.data[dp] = src.data[sp++] + dp += img._width + } + } + + export function clone(img: RefImage) { + let r = new RefImage(img._width, img._height, img._bpp) + r.data.set(img.data) + return r + } + + export function flipX(img: RefImage) { + img.makeWritable() + const w = img._width + const h = img._height + for (let i = 0; i < h; ++i) { + img.data.subarray(i * w, (i + 1) * w).reverse() + } + } + + + export function flipY(img: RefImage) { + img.makeWritable() + const w = img._width + const h = img._height + const d = img.data + for (let i = 0; i < w; ++i) { + let top = i + let bot = i + (h - 1) * w + while (top < bot) { + let c = d[top] + d[top] = d[bot] + d[bot] = c + top += w + bot -= w + } + } + } + + export function transposed(img: RefImage) { + const w = img._width + const h = img._height + const d = img.data + const r = new RefImage(h, w, img._bpp) + const n = r.data + let src = 0 + + for (let i = 0; i < h; ++i) { + let dst = i + for (let j = 0; j < w; ++j) { + n[dst] = d[src++] + dst += w + } + } + + return r + } + + export function copyFrom(img: RefImage, from: RefImage) { + if (img._width != from._width || img._height != from._height || + img._bpp != from._bpp) + return; + img.data.set(from.data) + } + + export function scroll(img: RefImage, dx: number, dy: number) { + img.makeWritable() + dx |= 0 + dy |= 0 + if (dx != 0) { + const img2 = clone(img) + img.data.fill(0) + drawTransparentImage(img, img2, dx, dy) + } else if (dy < 0) { + dy = -dy + if (dy < img._height) + img.data.copyWithin(0, dy * img._width) + else + dy = img._height + img.data.fill(0, (img._height - dy) * img._width) + } else if (dy > 0) { + if (dy < img._height) + img.data.copyWithin(dy * img._width, 0) + else + dy = img._height + img.data.fill(0, 0, dy * img._width) + } + // TODO implement dx + } + + export function replace(img: RefImage, from: number, to: number) { + to &= 0xf; + const d = img.data + for (let i = 0; i < d.length; ++i) + if (d[i] == from) d[i] = to + } + + export function doubledX(img: RefImage) { + const w = img._width + const h = img._height + const d = img.data + const r = new RefImage(w * 2, h, img._bpp) + const n = r.data + let dst = 0 + + for (let src = 0; src < d.length; ++src) { + let c = d[src] + n[dst++] = c + n[dst++] = c + } + + return r + } + + export function doubledY(img: RefImage) { + const w = img._width + const h = img._height + const d = img.data + const r = new RefImage(w, h * 2, img._bpp) + const n = r.data + + let src = 0 + let dst0 = 0 + let dst1 = w + for (let i = 0; i < h; ++i) { + for (let j = 0; j < w; ++j) { + let c = d[src++] + n[dst0++] = c + n[dst1++] = c + } + dst0 += w + dst1 += w + } + + return r + } + + + export function doubled(img: RefImage) { + return doubledX(doubledY(img)) + } + + function drawImageCore(img: RefImage, from: RefImage, x: number, y: number, clear: boolean, check: boolean) { + x |= 0 + y |= 0 + + const w = from._width + let h = from._height + const sh = img._height + const sw = img._width + + if (x + w <= 0) return false + if (x >= sw) return false + if (y + h <= 0) return false + if (y >= sh) return false + + if (clear) + fillRect(img, x, y, from._width, from._height, 0) + else if (!check) + img.makeWritable() + + const len = x < 0 ? Math.min(sw, w + x) : Math.min(sw - x, w) + const fdata = from.data + const tdata = img.data + + for (let p = 0; h--; y++ , p += w) { + if (0 <= y && y < sh) { + let dst = y * sw + let src = p + if (x < 0) + src += -x + else + dst += x + for (let i = 0; i < len; ++i) { + const v = fdata[src++] + if (v) { + if (check) { + if (tdata[dst]) + return true + } else { + tdata[dst] = v + } + } + dst++ + } + } + } + + return false + } + + export function drawImage(img: RefImage, from: RefImage, x: number, y: number) { + drawImageCore(img, from, x, y, true, false) + } + + export function drawTransparentImage(img: RefImage, from: RefImage, x: number, y: number) { + drawImageCore(img, from, x, y, false, false) + } + + export function overlapsWith(img: RefImage, other: RefImage, x: number, y: number) { + return drawImageCore(img, other, x, y, false, true) + } + + function drawLineLow(img: RefImage, x0: number, y0: number, x1: number, y1: number, c: number) { + let dx = x1 - x0; + let dy = y1 - y0; + let yi = img._width; + if (dy < 0) { + yi = -yi; + dy = -dy; + } + let D = 2 * dy - dx; + dx <<= 1; + dy <<= 1; + c = img.color(c); + let ptr = img.pix(x0, y0) + for (let x = x0; x <= x1; ++x) { + img.data[ptr] = c + if (D > 0) { + ptr += yi; + D -= dx; + } + D += dy; + ptr++; + } + } + + function drawLineHigh(img: RefImage, x0: number, y0: number, x1: number, y1: number, c: number) { + let dx = x1 - x0; + let dy = y1 - y0; + let xi = 1; + if (dx < 0) { + xi = -1; + dx = -dx; + } + let D = 2 * dx - dy; + dx <<= 1; + dy <<= 1; + c = img.color(c); + let ptr = img.pix(x0, y0); + for (let y = y0; y <= y1; ++y) { + img.data[ptr] = c; + if (D > 0) { + ptr += xi; + D -= dy; + } + D += dx; + ptr += img._width; + } + } + + export function _drawLine(img: RefImage, xy: number, wh: number, c: number) { + drawLine(img, XX(xy), YY(xy), XX(wh), YY(wh), c) + } + + export function drawLine(img: RefImage, x0: number, y0: number, x1: number, y1: number, c: number) { + x0 |= 0 + y0 |= 0 + x1 |= 0 + y1 |= 0 + + if (x1 < x0) { + drawLine(img, x1, y1, x0, y0, c); + return; + } + + let w = x1 - x0; + let h = y1 - y0; + + if (h == 0) { + if (w == 0) + setPixel(img, x0, y0, c); + else + fillRect(img, x0, y0, w + 1, 1, c); + return; + } + + if (w == 0) { + if (h > 0) + fillRect(img, x0, y0, 1, h + 1, c); + else + fillRect(img, x0, y1, 1, -h + 1, c); + return; + } + + if (x1 < 0 || x0 >= img._width) + return; + if (x0 < 0) { + y0 -= (h * x0 / w) | 0; + x0 = 0; + } + if (x1 >= img._width) { + let d = (img._width - 1) - x1; + y1 += (h * d / w) | 0; + x1 = img._width - 1 + } + + if (y0 < y1) { + if (y0 >= img._height || y1 < 0) + return; + if (y0 < 0) { + x0 -= (w * y0 / h) | 0; + y0 = 0; + } + if (y1 >= img._height) { + let d = (img._height - 1) - y1; + x1 += (w * d / h) | 0; + y1 = img._height + } + } else { + if (y1 >= img._height || y0 < 0) + return; + if (y1 < 0) { + x1 -= (w * y1 / h) | 0; + y1 = 0; + } + if (y0 >= img._height) { + let d = (img._height - 1) - y0; + x0 += (w * d / h) | 0; + y0 = img._height + } + } + + img.makeWritable() + + if (h < 0) { + h = -h; + if (h < w) + drawLineLow(img, x0, y0, x1, y1, c); + else + drawLineHigh(img, x1, y1, x0, y0, c); + } else { + if (h < w) + drawLineLow(img, x0, y0, x1, y1, c); + else + drawLineHigh(img, x0, y0, x1, y1, c); + } + } + + export function drawIcon(img: RefImage, icon: RefBuffer, x: number, y: number, color: number) { + const src: Uint8Array = icon.data + if (!simage.isValidImage(icon)) + return + if (src[1] != 1) + return // only mono + let width = simage.bufW(src) + let height = simage.bufH(src) + let byteH = simage.byteHeight(height, 1) + + x |= 0 + y |= 0 + const destHeight = img._height + const destWidth = img._width + + if (x + width <= 0) return + if (x >= destWidth) return + if (y + height <= 0) return + if (y >= destHeight) return + + img.makeWritable() + + let srcPointer = 8 + color = img.color(color) + const screen = img.data + + for (let i = 0; i < width; ++i) { + let destX = x + i + if (0 <= destX && destX < destWidth) { + let destIndex = destX + y * destWidth + let srcIndex = srcPointer + let destY = y + let destEnd = Math.min(destHeight, height + y) + if (y < 0) { + srcIndex += ((-y) >> 3) + destY += ((-y) >> 3) * 8 + destIndex += (destY - y) * destWidth + } + let mask = 0x01 + let srcByte = src[srcIndex++] + while (destY < destEnd) { + if (destY >= 0 && (srcByte & mask)) { + screen[destIndex] = color + } + mask <<= 1 + if (mask == 0x100) { + mask = 0x01 + srcByte = src[srcIndex++] + } + destIndex += destWidth + destY++ + } + } + srcPointer += byteH + } + } + + export function _drawIcon(img: RefImage, icon: RefBuffer, xy: number, color: number) { + drawIcon(img, icon, XX(xy), YY(xy), color) + } + + export function fillCircle(img: RefImage, cx: number, cy: number, r: number, c: number) { + let x = r - 1; + let y = 0; + let dx = 1; + let dy = 1; + let err = dx - (r << 1); + while (x >= y) { + fillRect(img, cx + x, cy - y, 1, 1 + (y << 1), c); + fillRect(img, cx + y, cy - x, 1, 1 + (x << 1), c); + fillRect(img, cx - x, cy - y, 1, 1 + (y << 1), c); + fillRect(img, cx - y, cy - x, 1, 1 + (x << 1), c); + if (err <= 0) { + y++; + err += dy; + dy += 2; + } + if (err > 0) { + x--; + dx += 2; + err += dx - (r << 1); + } + } + } + + export function _fillCircle(img: RefImage, cxy: number, r: number, c: number) { + fillCircle(img, XX(cxy), YY(cxy), r, c); + } + + interface LineGenState { + x: number; + y: number; + x0: number; + y0: number; + x1: number; + y1: number; + W: number; + H: number; + dx: number; + dy: number; + yi: number; + xi: number; + D: number; + nextFuncIndex: number; + } + interface ValueRange { + min: number; + max: number; + } + + function nextYRange_Low(x: number, line: LineGenState, yRange: ValueRange) { + while (line.x === x && line.x <= line.x1 && line.x < line.W) { + if (0 <= line.x) { + if (line.y < yRange.min) yRange.min = line.y; + if (line.y > yRange.max) yRange.max = line.y + } + if (line.D > 0) { + line.y += line.yi; + line.D -= line.dx; + } + line.D += line.dy; + ++line.x; + } + } + + function nextYRange_HighUp(x: number, line: LineGenState, yRange: ValueRange) { + while (line.x == x && line.y >= line.y1 && line.x < line.W) { + if (0 <= line.x) { + if (line.y < yRange.min) yRange.min = line.y; + if (line.y > yRange.max) yRange.max = line.y; + } + if (line.D > 0) { + line.x += line.xi; + line.D += line.dy; + } + line.D += line.dx; + --line.y; + } + } + + function nextYRange_HighDown(x: number, line: LineGenState, yRange: ValueRange) { + while (line.x == x && line.y <= line.y1 && line.x < line.W) { + if (0 <= line.x) { + if (line.y < yRange.min) yRange.min = line.y; + if (line.y > yRange.max) yRange.max = line.y; + } + if (line.D > 0) { + line.x += line.xi; + line.D -= line.dy; + } + line.D += line.dx; + ++line.y; + } + } + + function initYRangeGenerator(X0: number, Y0: number, X1: number, Y1: number): LineGenState { + const line: LineGenState = { + x: X0, + y: Y0, + x0: X0, + y0: Y0, + x1: X1, + y1: Y1, + W: 0, + H: 0, + dx: X1 - X0, + dy: Y1 - Y0, + yi: 0, + xi: 0, + D: 0, + nextFuncIndex: 0, + }; + + if ((line.dy < 0 ? -line.dy : line.dy) < line.dx) { + line.yi = 1; + if (line.dy < 0) { + line.yi = -1; + line.dy = -line.dy; + } + line.D = 2 * line.dy - line.dx; + line.dx = line.dx << 1; + line.dy = line.dy << 1; + + line.nextFuncIndex = 0; + return line; + } else { + line.xi = 1; + if (line.dy < 0) { + line.D = 2 * line.dx + line.dy; + line.dx = line.dx << 1; + line.dy = line.dy << 1; + + line.nextFuncIndex = 1; + return line; + } else { + line.D = 2 * line.dx - line.dy; + line.dx = line.dx << 1; + line.dy = line.dy << 1; + + line.nextFuncIndex = 2; + return line; + } + } + } + + export function fillTriangle(img: RefImage, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, c: number) { + if (x1 < x0) { + [x1, x0] = [x0, x1]; + [y1, y0] = [y0, y1]; + } + if (x2 < x1) { + [x2, x1] = [x1, x2]; + [y2, y1] = [y1, y2]; + } + if (x1 < x0) { + [x1, x0] = [x0, x1]; + [y1, y0] = [y0, y1]; + } + + const lines: LineGenState[] = [ + initYRangeGenerator(x0, y0, x2, y2), + initYRangeGenerator(x0, y0, x1, y1), + initYRangeGenerator(x1, y1, x2, y2) + ]; + + lines[0].W = lines[1].W = lines[2].W = width(img); + lines[0].H = lines[1].H = lines[2].H = height(img); + + type FP_NEXT = (x: number, line: LineGenState, yRange: ValueRange) => void; + const nextFuncList: FP_NEXT[] = [ + nextYRange_Low, + nextYRange_HighUp, + nextYRange_HighDown + ]; + const fpNext0 = nextFuncList[lines[0].nextFuncIndex]; + const fpNext1 = nextFuncList[lines[1].nextFuncIndex]; + const fpNext2 = nextFuncList[lines[2].nextFuncIndex]; + + const yRange= { + min: lines[0].H, + max: -1 + }; + + for (let x = lines[1].x0; x <= lines[1].x1; x++) { + yRange.min = lines[0].H; yRange.max = -1; + fpNext0(x, lines[0], yRange); + fpNext1(x, lines[1], yRange); + fillRect(img, x, yRange.min, 1, yRange.max - yRange.min + 1, c); + } + + fpNext2(lines[2].x0, lines[2], yRange); + + for (let x = lines[2].x0 + 1; x <= lines[2].x1; x++) { + yRange.min = lines[0].H; yRange.max = -1; + fpNext0(x, lines[0], yRange); + fpNext2(x, lines[2], yRange); + fillRect(img, x, yRange.min, 1, yRange.max - yRange.min + 1, c); + } + } + + export function _fillTriangle(img: RefImage, args: RefCollection) { + fillTriangle( + img, + args.getAt(0) | 0, + args.getAt(1) | 0, + args.getAt(2) | 0, + args.getAt(3) | 0, + args.getAt(4) | 0, + args.getAt(5) | 0, + args.getAt(6) | 0, + ); + } + + export function fillPolygon4(img: RefImage, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, c: number) { + const lines: LineGenState[]= [ + (x0 < x1) ? initYRangeGenerator(x0, y0, x1, y1) : initYRangeGenerator(x1, y1, x0, y0), + (x1 < x2) ? initYRangeGenerator(x1, y1, x2, y2) : initYRangeGenerator(x2, y2, x1, y1), + (x2 < x3) ? initYRangeGenerator(x2, y2, x3, y3) : initYRangeGenerator(x3, y3, x2, y2), + (x0 < x3) ? initYRangeGenerator(x0, y0, x3, y3) : initYRangeGenerator(x3, y3, x0, y0) + ]; + + lines[0].W = lines[1].W = lines[2].W = lines[3].W = width(img); + lines[0].H = lines[1].H = lines[2].H = lines[3].H = height(img); + + let minX = Math.min(Math.min(x0, x1), Math.min(x2, x3)); + let maxX = Math.min(Math.max(Math.max(x0, x1), Math.max(x2, x3)), lines[0].W - 1); + + type FP_NEXT = (x: number, line: LineGenState, yRange: ValueRange) => void; + const nextFuncList: FP_NEXT[] = [ + nextYRange_Low, + nextYRange_HighUp, + nextYRange_HighDown + ]; + + const fpNext0 = nextFuncList[lines[0].nextFuncIndex]; + const fpNext1 = nextFuncList[lines[1].nextFuncIndex]; + const fpNext2 = nextFuncList[lines[2].nextFuncIndex]; + const fpNext3 = nextFuncList[lines[3].nextFuncIndex]; + + const yRange: ValueRange = { + min: lines[0].H, + max: -1 + }; + + for (let x = minX; x <= maxX; x++) { + yRange.min = lines[0].H; yRange.max = -1; + fpNext0(x, lines[0], yRange); + fpNext1(x, lines[1], yRange); + fpNext2(x, lines[2], yRange); + fpNext3(x, lines[3], yRange); + fillRect(img, x,yRange.min, 1, yRange.max - yRange.min + 1, c); + } + } + + export function _fillPolygon4(img: RefImage, args: RefCollection) { + fillPolygon4( + img, + args.getAt(0) | 0, + args.getAt(1) | 0, + args.getAt(2) | 0, + args.getAt(3) | 0, + args.getAt(4) | 0, + args.getAt(5) | 0, + args.getAt(6) | 0, + args.getAt(7) | 0, + args.getAt(8) | 0, + ); + } + + export function _blitRow(img: RefImage, xy: number, from: RefImage, xh: number) { + blitRow(img, XX(xy), YY(xy), from, XX(xh), YY(xh)) + } + + export function blitRow(img: RefImage, x: number, y: number, from: RefImage, fromX: number, fromH: number) { + x |= 0 + y |= 0 + fromX |= 0 + fromH |= 0 + if (!img.inRange(x, 0) || !img.inRange(fromX, 0) || fromH <= 0) + return + let fy = 0 + let stepFY = ((from._width << 16) / fromH) | 0 + let endY = y + fromH + if (endY > img._height) + endY = img._height + if (y < 0) { + fy += -y * stepFY + y = 0 + } + while (y < endY) { + img.data[img.pix(x, y)] = from.data[from.pix(fromX, fy >> 16)] + y++ + fy += stepFY + } + } + + export function _blit(img: RefImage, src: RefImage, args: RefCollection): boolean { + return blit(img, src, args); + } + + export function blit(dst: RefImage, src: RefImage, args: RefCollection): boolean { + const xDst = args.getAt(0) as number; + const yDst = args.getAt(1) as number; + const wDst = args.getAt(2) as number; + const hDst = args.getAt(3) as number; + const xSrc = args.getAt(4) as number; + const ySrc = args.getAt(5) as number; + const wSrc = args.getAt(6) as number; + const hSrc = args.getAt(7) as number; + const transparent = args.getAt(8) as number; + const check = args.getAt(9) as number; + + const xSrcStep = ((wSrc << 16) / wDst) | 0; + const ySrcStep = ((hSrc << 16) / hDst) | 0; + + const xDstClip = Math.abs(Math.min(0, xDst)); + const yDstClip = Math.abs(Math.min(0, yDst)); + const xDstStart = xDst + xDstClip; + const yDstStart = yDst + yDstClip; + const xDstEnd = Math.min(dst._width, xDst + wDst); + const yDstEnd = Math.min(dst._height, yDst + hDst); + + const xSrcStart = Math.max(0, (xSrc << 16) + xDstClip * xSrcStep); + const ySrcStart = Math.max(0, (ySrc << 16) + yDstClip * ySrcStep); + const xSrcEnd = Math.min(src._width, xSrc + wSrc) << 16; + const ySrcEnd = Math.min(src._height, ySrc + hSrc) << 16; + + if (!check) + dst.makeWritable(); + + for (let yDstCur = yDstStart, ySrcCur = ySrcStart; yDstCur < yDstEnd && ySrcCur < ySrcEnd; ++yDstCur, ySrcCur += ySrcStep) { + const ySrcCurI = ySrcCur >> 16; + for (let xDstCur = xDstStart, xSrcCur = xSrcStart; xDstCur < xDstEnd && xSrcCur < xSrcEnd; ++xDstCur, xSrcCur += xSrcStep) { + const xSrcCurI = xSrcCur >> 16; + const cSrc = getPixel(src, xSrcCurI, ySrcCurI); + if (check && cSrc) { + const cDst = getPixel(dst, xDstCur, yDstCur); + if (cDst) { + return true; + } + continue; + } + if (!transparent || cSrc) { + setPixel(dst, xDstCur, yDstCur, cSrc); + } + } + } + return false; + } +} + + +namespace pxsim.simage { + export function byteHeight(h: number, bpp: number) { + if (bpp == 1) + return h * bpp + 7 >> 3 + else + return ((h * bpp + 31) >> 5) << 2 + } + + function isLegacyImage(buf: RefBuffer) { + if (!buf || buf.data.length < 5) + return false; + + if (buf.data[0] != 0xe1 && buf.data[0] != 0xe4) + return false; + + const bpp = buf.data[0] & 0xf; + const sz = buf.data[1] * byteHeight(buf.data[2], bpp) + if (4 + sz != buf.data.length) + return false; + + return true; + } + + export function bufW(data: Uint8Array) { + return data[2] | (data[3] << 8) + } + + export function bufH(data: Uint8Array) { + return data[4] | (data[5] << 8) + } + + export function isValidImage(buf: RefBuffer) { + if (!buf || buf.data.length < 5) + return false; + + if (buf.data[0] != 0x87) + return false + + if (buf.data[1] != 1 && buf.data[1] != 4) + return false; + + const bpp = buf.data[1]; + const sz = bufW(buf.data) * byteHeight(bufH(buf.data), bpp) + if (8 + sz != buf.data.length) + return false; + + return true; + } + + + export function create(w: number, h: number) { + // truncate decimal sizes + w |= 0 + h |= 0 + return new RefImage(w, h, getScreenState().bpp()) + } + + export function ofBuffer(buf: RefBuffer): RefImage { + const src: Uint8Array = buf.data + + let srcP = 4 + let w = 0, h = 0, bpp = 0 + + if (isLegacyImage(buf)) { + w = src[1] + h = src[2] + bpp = src[0] & 0xf; + // console.log("using legacy image") + } else if (isValidImage(buf)) { + srcP = 8 + w = bufW(src) + h = bufH(src) + bpp = src[1] + } + + if (w == 0 || h == 0) + return null + const r = new RefImage(w, h, bpp) + const dst = r.data + + r.isStatic = buf.isStatic + + if (bpp == 1) { + for (let i = 0; i < w; ++i) { + let dstP = i + let mask = 0x01 + let v = src[srcP++] + for (let j = 0; j < h; ++j) { + if (mask == 0x100) { + mask = 0x01 + v = src[srcP++] + } + if (v & mask) + dst[dstP] = 1 + dstP += w + mask <<= 1 + } + } + } else if (bpp == 4) { + for (let i = 0; i < w; ++i) { + let dstP = i + for (let j = 0; j < h >> 1; ++j) { + const v = src[srcP++] + dst[dstP] = v & 0xf + dstP += w + dst[dstP] = v >> 4 + dstP += w + } + if (h & 1) + dst[dstP] = src[srcP++] & 0xf + srcP = (srcP + 3) & ~3 + } + } + + return r + } + + export function toBuffer(img: RefImage): RefBuffer { + let col = byteHeight(img._height, img._bpp) + let sz = 8 + img._width * col + let r = new Uint8Array(sz) + r[0] = 0x87 + r[1] = img._bpp + r[2] = img._width & 0xff + r[3] = img._width >> 8 + r[4] = img._height & 0xff + r[5] = img._height >> 8 + let dstP = 8 + const w = img._width + const h = img._height + const data = img.data + for (let i = 0; i < w; ++i) { + if (img._bpp == 4) { + let p = i + for (let j = 0; j < h; j += 2) { + r[dstP++] = ((data[p + w] & 0xf) << 4) | ((data[p] || 0) & 0xf) + p += 2 * w + } + dstP = (dstP + 3) & ~3 + } else if (img._bpp == 1) { + let mask = 0x01 + let p = i + for (let j = 0; j < h; j++) { + if (data[p]) + r[dstP] |= mask + mask <<= 1 + p += w + if (mask == 0x100) { + mask = 0x01 + dstP++ + } + } + if (mask != 0x01) + dstP++ + } + } + + return new RefBuffer(r) + } + + export function doubledIcon(buf: RefBuffer): RefBuffer { + let img = ofBuffer(buf) + if (!img) + return null + img = SImageMethods.doubled(img) + return toBuffer(img) + } +} + +namespace pxsim.pxtcore { + export function updateScreen(img: RefImage) { + const state = getScreenState(); + if (state) + state.showImage(img) + } + export function updateStats(s: string) { + const state = getScreenState(); + if (state) + state.updateStats(s); + } + export function setPalette(b: RefBuffer) { + const state = getScreenState(); + if (state) + state.setPalette(b) + } + export function setupScreenStatusBar(barHeight: number) { + const state = getScreenState(); + if (state) + state.setupScreenStatusBar(barHeight); + } + export function updateScreenStatusBar(img: RefImage) { + const state = getScreenState(); + if (state) + state.updateScreenStatusBar(img); + } + export function setScreenBrightness(b: number) { + // I guess we could at least turn the screen off, when b==0, + // otherwise, it probably doesn't make much sense to do anything. + const state = getScreenState(); + if (state) + state.setScreenBrightness(b); + } +} diff --git a/libs/screen/sim/part.svg b/libs/screen/sim/part.svg new file mode 100644 index 00000000000..45ae48b314c --- /dev/null +++ b/libs/screen/sim/part.svg @@ -0,0 +1,259 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + Gnd + VCC + D/C + CS + MOSI + SCK + BL + + RST + + MISO + + diff --git a/libs/screen/sim/state.ts b/libs/screen/sim/state.ts new file mode 100644 index 00000000000..09985c44987 --- /dev/null +++ b/libs/screen/sim/state.ts @@ -0,0 +1,185 @@ +namespace pxsim { + function htmlColorToUint32(hexColor: string) { + const ca = new Uint8ClampedArray(4) + const v = parseInt(hexColor.replace(/#/, ""), 16) + ca[0] = (v >> 16) & 0xff; + ca[1] = (v >> 8) & 0xff; + ca[2] = (v >> 0) & 0xff; + ca[3] = 0xff; // alpha + // convert to uint32 using target endian + return new Uint32Array(ca.buffer)[0] + } + + function UInt32ToRGB(col: number): number[] { + const ui = new Uint32Array(1); + ui[0] = col; + const ca = new Uint8ClampedArray(ui.buffer); + return [ca[0], ca[1], ca[2]]; + } + + export class ScreenState { + width = 0 + height = 0 + screen: Uint32Array + palette: Uint32Array + lastImage: RefImage + lastImageFlushTime = 0 + changed = true + stats: string; + brightness = 255; + onChange = () => { } + + constructor(paletteSrc: string[], w = 0, h = 0) { + if (!paletteSrc) paletteSrc = ["#000000", "#ffffff"] + this.palette = new Uint32Array(paletteSrc.length); + this.setPaletteFromHtmlColors(paletteSrc); + if (w) { + this.width = w + this.height = h + this.screen = new Uint32Array(this.width * this.height) + this.screen.fill(this.palette[0]) + } + } + + setScreenBrightness(b: number) { + this.brightness = b | 0; + } + + paletteToUint8Array() { + const out = new Uint8Array(this.palette.length * 3); + for (let i = 0; i < this.palette.length; ++i) { + const [r, g, b] = UInt32ToRGB(this.palette[i]); + const s = 3 * i; + out[s] = r; + out[s + 1] = g; + out[s + 2] = b; + } + return out; + } + + setPaletteFromHtmlColors(src: string[]) { + for (let i = 0; i < this.palette.length; ++i) { + this.palette[i] = htmlColorToUint32(src[i]) + } + } + + setPalette(buf: RefBuffer) { + const ca = new Uint8ClampedArray(4) + const rd = new Uint32Array(ca.buffer) + const src = buf.data as Uint8Array + if (48 != src.length) + pxsim.pxtrt.panic(pxsim.PXT_PANIC.PANIC_SCREEN_ERROR); + + this.palette = new Uint32Array((src.length / 3) | 0) + for (let i = 0; i < this.palette.length; ++i) { + const p = i * 3 + ca[0] = src[p + 0] + ca[1] = src[p + 1] + ca[2] = src[p + 2] + ca[3] = 0xff // alpha + // convert to uint32 using target endian + this.palette[i] = rd[0] + } + } + + bpp() { + return this.palette.length > 2 ? 4 : 1 + } + + didChange() { + let res = this.changed + this.changed = false + return res + } + + maybeForceUpdate() { + if (Date.now() - this.lastImageFlushTime > 200) { + this.showImage(null) + } + } + + showImage(img: RefImage) { + runtime.startPerfCounter(0) + if (!img) + img = this.lastImage + + if (!img) + return + + if (this.width == 0) { + this.width = img._width + this.height = img._height + + this.screen = new Uint32Array(this.width * this.height) + } + + this.lastImageFlushTime = Date.now() + this.lastImage = img + this.changed = true + + const src = img.data + const dst = this.screen + if (this.width != img._width || this.height != img._height || src.length != dst.length) + U.userError("wrong size") + const p = this.palette + const mask = p.length - 1 + for (let i = 0; i < src.length; ++i) { + dst[i] = p[src[i] & mask] + } + + this.onChange() + runtime.stopPerfCounter(0) + } + + updateStats(stats: string) { + this.stats = stats; + const b = (board() as any); + if (b && b.updateStats) { + b.updateStats(); + } + } + + bindToSvgImage(lcd: SVGImageElement) { + const screenCanvas = document.createElement("canvas"); + screenCanvas.width = this.width + screenCanvas.height = this.height + + const ctx = screenCanvas.getContext("2d") + ctx.imageSmoothingEnabled = false + const imgdata = ctx.getImageData(0, 0, this.width, this.height) + const arr = new Uint32Array(imgdata.data.buffer) + + const flush = function () { + requested = false + ctx.putImageData(imgdata, 0, 0) + lcd.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", screenCanvas.toDataURL()); + } + + let requested = false; + this.onChange = () => { + arr.set(this.screen) + // paint rect + runtime.queueDisplayUpdate(); + if (!requested) { + requested = true + window.requestAnimationFrame(flush) + } + } + } + + setupScreenStatusBar(barHeight: number) { + // TODO + } + updateScreenStatusBar(img: RefImage) { + // TODO + } + } + + export interface ScreenBoard extends CommonBoard { + screenState: ScreenState; + } + + export function getScreenState() { + return (board() as ScreenBoard).screenState + } +} \ No newline at end of file diff --git a/libs/screen/sim/tsconfig.json b/libs/screen/sim/tsconfig.json new file mode 100644 index 00000000000..1db1f5ab97a --- /dev/null +++ b/libs/screen/sim/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2017", + "noImplicitAny": true, + "noImplicitReturns": true, + "lib": [ + "DOM", + "DOM.Iterable", + "ES2017" + ], + "rootDir": ".", + "newLine": "LF", + "sourceMap": false + } +} diff --git a/libs/screen/sim/visual.ts b/libs/screen/sim/visual.ts new file mode 100644 index 00000000000..2f0307d0e48 --- /dev/null +++ b/libs/screen/sim/visual.ts @@ -0,0 +1,108 @@ + +namespace pxsim.visuals { + const SCREEN_PART_WIDTH = 158.439; + const SCREEN_PART_HEIGHT = 146.803; + const SCREEN_PART = ` + + + + + + + + + + + + + Gnd + + + VCC + + + D/C + + + CS + + + MOSI + + + SCK + + + BL + + + + RST + + + + MISO + + + + `; + + export function mkScreenPart(xy: Coord = [0, 0]): SVGElAndSize { + let [x, y] = xy; + let l = x; + let t = y; + let w = SCREEN_PART_WIDTH; + let h = SCREEN_PART_HEIGHT; + let img = svg.elt("image"); + svg.hydrate(img, { + class: "sim-screen", x: l, y: t, width: w, height: h, + href: svg.toDataUri(SCREEN_PART) + }); + return { el: img, x: l, y: t, w: w, h: h }; + } + + export class ScreenView implements IBoardPart { + bus: pxsim.EventBus; + style: string; + element: SVGElement; + overElement?: SVGElement; + defs: SVGElement[]; + state: ScreenState; + canvas: SVGImageElement; + lastLocation: Coord; + + constructor() { + + } + + init(bus: EventBus, state: ScreenState, svgEl: SVGSVGElement, otherParams: Map): void { + this.bus = bus; + this.state = state; + this.overElement = undefined; + this.defs = []; + this.lastLocation = [0, 0]; + + const partSvg = svg.parseString(SCREEN_PART); + this.canvas = partSvg.getElementById('thescreen') as SVGImageElement; + this.element = svg.elt("g"); + this.element.appendChild(partSvg.firstElementChild as SVGElement); + this.state.bindToSvgImage(this.canvas); + } + + moveToCoord(xy: visuals.Coord): void { + let [x, y] = xy; + const loc: Coord = [x, y]; + this.lastLocation = loc; + this.updateLoc(); + } + + private updateLoc() { + let [x, y] = this.lastLocation; + translateEl(this.element, [x, y]) + } + + updateState(): void { } + + updateTheme(): void { } + } +} diff --git a/libs/screen/targetoverrides.ts b/libs/screen/targetoverrides.ts new file mode 100644 index 00000000000..0afc43cde1d --- /dev/null +++ b/libs/screen/targetoverrides.ts @@ -0,0 +1,13 @@ +// This file would be usually overridden by the target. +const screen = simage.create(178, 128) as ScreenImage + +namespace _screen_internal { + //% shim=pxt::updateScreen + function updateScreen(img: SImage): void {} + //% shim=pxt::updateStats + function updateStats(msg: string): void {} + control.__screen.setupUpdate(() => updateScreen(screen)) + //control.EventContext.onStats = function(msg: string) { + // updateStats(msg); + //} +} diff --git a/libs/screen/text.ts b/libs/screen/text.ts new file mode 100644 index 00000000000..fbfb592af62 --- /dev/null +++ b/libs/screen/text.ts @@ -0,0 +1,308 @@ +namespace simage { + + export interface Font { + charWidth: number; + charHeight: number; + data: Buffer; + multiplier?: number; + } + + //% whenUsed + export const font8: Font = { + charWidth: 6, + charHeight: 8, + data: hex` +2000000000000000 210000005e000000 2200000e000e0000 230028fe28fe2800 24004c92ff926400 250002651248a640 +26006c92926ca000 270000000e000000 280000007c820000 29000000827c0000 2a00543810385400 2b0010107c101000 +2c00000090700000 2d00101010101000 2e00000060600000 2f00006010080600 3000003c42423c00 310000447e400000 +3200004462524c00 330000424a4e3200 34003028247e2000 3500004e4a4a3200 3600003c4a4a3000 3700000262120e00 +380000344a4a3400 3900000c52523c00 3a0000006c6c0000 3b00000096760000 3c00102828444400 3d00282828282800 +3e00444428281000 3f00000259090600 40003c425a560800 4100781412147800 42007e4a4a4a3400 4300003c42422400 +4400007e42423c00 4500007e4a4a4200 4600007e0a0a0200 4700003c42523400 4800007e08087e00 490000427e420000 +4a002040423e0200 4b00007e08146200 4c00007e40404000 4d007e0418047e00 4e00007e04087e00 4f003c4242423c00 +5000007e12120c00 5100003c5262bc00 5200007e12126c00 530000244a522400 540002027e020200 5500003e40403e00 +5600001e70701e00 57007e2018207e00 5800422418244200 5900060870080600 5a000062524a4600 5b00007e42420000 +5c00000608106000 5d000042427e0000 5e00080402040800 5f00808080808000 6000000002040000 6100003048487800 +6200007e48483000 6300003048484800 6400003048487e00 6500003068585000 660000107c120400 67000018a4a47800 +6800007e08087000 690000487a400000 6a000040847d0000 6b00007e10284000 6c0000427e400000 6d00780830087000 +6e00007808087000 6f00003048483000 700000fc24241800 710000182424fc00 7200007810081000 7300005058682800 +740000083e482000 7500003840407800 7600001860601800 7700384030403800 7800004830304800 7900005ca0a07c00 +7a00004868584800 7b00000836410000 7c000000fe000000 7d00004136080000 7e00000804080400 a000000000000000 +a10000007a000000 a200003048fc4800 a30090fc92928400 a400542844285400 a5002a2c782c2a00 a6000000ee000000 +a7000094aaaa5200 a800000200020000 a9003e414955413e aa0000242a2e0000 ab00102854284400 ac00001010107000 +ad00001010101000 ae003e415d45413e af00000202020200 b000000814140800 b1008888be888800 b2000024322c0000 +b30000222a140000 b400000004020000 b50000f840207800 b6000c1e7e027e00 b700000010000000 b800000080400000 +b90000243e200000 ba0000242a240000 bb00442854281000 bc00025f70f84000 bd00021f90c8b000 be0011557af84000 +bf000030484d2000 c000601916186000 c100601816196000 c200601a151a6000 c300601a151a6100 c400601914196000 +c500601a151a6000 c6007c0a7e4a4200 c700001ea1611200 c800007c55564400 c900007c56554400 ca00007c56554600 +cb00007c55544500 cc0000457e440000 cd0000447e450000 ce0000467d460000 cf0000457c450000 d000087e4a423c00 +d100007e09127d00 d200003845463800 d300003846453800 d400003846453a00 d500003a45463900 d600003845443900 +d700442810284400 d80000fc724e3f00 d900003c41423c00 da00003c42413c00 db00003c42413e00 dc00003c41403d00 +dd00040872090400 de00007e24241800 df00007c025a2400 e0000030494a7800 e10000304a497800 e20000304a497a00 +e3000032494a7900 e40000304a487a00 e50000304a4d7a00 e600304878685000 e7000018a4642400 e8000030695a5000 +e90000306a595000 ea0000306a595200 eb0000306a585200 ec0000497a400000 ed0000487a410000 ee00004a79420000 +ef00004a78420000 f00000304a4b3d00 f100007a090a7100 f2000030494a3000 f30000304a493000 f40000304a493200 +f5000032494a3100 f60000304a483200 f700101054101000 f800007068583800 f900003841427800 fa00003842417800 +fb00003842417a00 fc00003842407a00 fd0000b84241f800 fe0000ff24241800 ff00005ca1a07d00 0001601915196000 +010100304a4a7a00 0201611a16196000 030100314a4a7900 04013c0a094abc00 050100182464bc00 0601003846452800 +070100304a494800 0801003846452a00 090100304a494a00 0a01003844452800 0b010030484a4800 0c01003845462900 +0d010030494a4900 0e01007c45463900 0f0100314a497e00 1001087e4a423c00 110130484c7e0400 1201007d55554500 +130100326a5a5200 1401007d56564500 150100316a5a5100 1601007c55544400 170100306a585000 1801003f65a52100 +1901001874ac2800 1a01007c55564500 1b010030695a5100 1c01003846553600 1d0100304a49f200 1e01003946563500 +1f0100314a4af100 2001003844553400 21010018a4a57800 2201001ea1691a00 23010018a6a57800 2401007812117a00 +25017e080a710200 2601047e147e0400 2701047e0c087000 28010002457e4500 29010002497a4100 2a0100457d450000 +2b01004a7a420000 2c0100014a7a4900 2d0100014a7a4100 2e0100217fa10000 2f0100247da00000 300100447d440000 +3101004878400000 32017e0022423e00 33013d0040847d00 34012040463d0600 350100800af90200 360100bf440a3100 +370100bf48142000 3801007810284800 3901007c40424100 3a0100467d400000 3b01003fa0602000 3c0100a17f200000 +3d01007c41424100 3e0100457e410000 3f01007e40484000 400100427e400800 4101107e48404000 420100527e480000 +4301007c0a117c00 440100780a097000 450100bf42043f00 460100bc44043800 4701007c09127d00 480100790a097000 +49010a0678087000 4a01003f02847f00 4b01003c04847800 4c01394545453900 4d0100324a4a3200 4e01394646463900 +4f0100314a4a3100 50013a4544463900 5101324948320100 52013c427e4a4200 5301304830685000 5401007c16354800 +5501007812091000 560100bf49093600 570100bc48040800 5801007d16354800 5901007912091000 5a01004856552400 +5b0100505a692800 5c01004856552600 5d0100505a692a00 5e010012a5691200 5f010028ac741400 6001004855562500 +61010050596a2900 62010101bf410100 630100049f641000 640104057e050400 650100083d4a2100 660102127e120200 +670100183e582000 6801003a41423900 6901003a41427900 6a01003d41413d00 6b01003a42427a00 6c01003942423900 +6d01003942427900 6e01003a45453a00 6f01003a45457a00 70013a41403a0100 71013a41407a0100 7201001f60a01f00 +7301001c60a03c00 7401782211227800 7501384231423800 7601081261120800 770100b84241fa00 7801040970090400 +79010064564d4400 7a0100486a594800 7b010064544d4400 7c010048685a4800 7d010064554e4500 7e010048695a4900 +7f0100087c020400 8f01003452523c00 920100887e090200 a0013c42423c0806 a101003048483008 af01003e403e0806 +b001003840781008 b501006a5a4a4e00 b601005878585800 d101003845463900 d2010030494a3100 e601003845563500 +e7010030494af100 fa0100742a750000 fb0100304c4a7d00 fc0178147e554400 fd0130487a695000 fe010078744e3d00 +ff0100706a593800 18020012a5691200 19020028ac741400 1a020101bf410100 1b0200049f641000 bb0200000c0a0000 +bc0200000a060000 bd020000060a0000 c602000201020000 c702000102010000 c902000202020000 d802000102020100 +d902000002000000 da02000205020000 db02000040800000 dc02000201020100 dd02020100020100 7403000002010000 +7503000080400000 7a030000c0800000 7e03000096760000 8403000003000000 8503020003000200 8603037c12127c00 +8703000010000000 880303007e4a4200 890303007e087e00 8a030300427e4200 8c03033c42423c00 8e0303000e700e00 +8f03035c62625c00 900302003b400200 9103781412147800 92037e4a4a4a3400 9303007e02020200 9403605846586000 +9503007e4a4a4200 96030062524a4600 9703007e08087e00 98033c4a4a4a3c00 990300427e420000 9a03007e08146200 +9b03601806186000 9c037e0418047e00 9d03007e04087e00 9e0300424a4a4200 9f033c4242423c00 a003007e02027e00 +a103007e12120c00 a30300665a424200 a40302027e020200 a503060870080600 a60318247e241800 a703422418244200 +a8030e107e100e00 a9035c6202625c00 aa0300457c450000 ab03040970090400 ac030030484a7900 ad030030685a5100 +ae0378100a09f000 af03003a41200000 b0033a4043403a00 b103003048487800 b20300fe25251a00 b3030c30c0300c00 +b403344a4a4a3000 b503003068585000 b603021aa6a24200 b7033c080404f800 b803003c4a4a3c00 b903003840200000 +ba03007820504800 bb03641212227c00 bc03fc2020103c00 bd03182040201800 be03112d2ba94100 bf03003048483000 +c003087808780800 c103f82424241800 c2031824a4a44800 c303304848582800 c403000838482800 c503384040403800 +c6031c20f8241800 c703c4281028c400 c8031c20fc201c00 c903304820483000 ca03000238422000 cb03384240423800 +cc0330484a493000 cd03384042413800 ce03304822493000 d0033c52525c2000 d10310344a3c0800 d203067804020400 +d303120a7c020400 d4030d7009040800 d5031824ff241800 d603384828483800 d70348302221d800 da031c2221a14200 +db031824a4a44200 dc037e1212020200 dd0300fc24240400 de033e2010087c00 df030c0ac9281800 e003700c621c7000 +e10301092516f800 e2039ea0bea07e00 e30398a0b8a07800 e4030c1214107e00 e503001028207800 e603be9088887000 +e70348544e443800 e803245252524c00 e903285454544800 ea0364524c526400 eb03086458640800 ec03385454542200 +ed03306848682400 ee03184a7e4a1800 ef031848ff0a0800 f003483020205800 f10378a4a4a49800 f203304848485000 +f303006080847d00 f4033c4a4a4a3c00 f503003058584800 0004007c55564400 0104007c55544500 020401013f857900 +0304007c06050400 04043c4a4a422400 050400244a522400 060400427e420000 070400457c450000 08042040423e0200 +09047c027e483000 0a047e087e483000 0b0402027e0a7200 0c04007c102a4500 0d047c2112087c00 0e040c5152523d00 +0f043f20e0203f00 1004781412147800 11047e4a4a4a3000 12047e4a4a4a3400 1304007e02020200 1404c07c427ec000 +1504007e4a4a4200 160476087e087600 170424424a4a3400 1804007e08047e00 1904007d120a7d00 1a04007e08146200 +1b04403c02027e00 1c047e0418047e00 1d04007e08087e00 1e043c4242423c00 1f047e0202027e00 2004007e12120c00 +2104003c42422400 220402027e020200 23040e5050503e00 240418247e241800 2504422418244200 26043f2020bf6000 +27040e1010107e00 28047e407e407e00 29043f203fa07f00 2a04027e48483000 2b047e4848307e00 2c04007e48483000 +2d0424424a4a3c00 2e047e183c423c00 2f04006c12127e00 3004304848784000 3104003c4a4a3100 3204007868502000 +3304007808080800 3404c0704878c000 3504306868500000 3604483078304800 3704004058683000 3804784020107800 +3904794222127900 3a04007820304800 3b04403008087800 3c04781020107800 3d04781010107800 3e04304848483000 +3f04780808087800 4004fc2424241800 4104304848485000 4204080878080800 43041ca0a0a07c00 44041824ff241800 +4504004830304800 46043c2020bc6000 4704182020207800 4804784078407800 49043c203ca07c00 4a04087850502000 +4b04785050207800 4c04007850502000 4d04485868300000 4e04783030483000 4f04502828780000 50040030696a5000 +51040032686a5000 5204023f0a887000 530400780a090800 5404003068584800 5504005058682800 560400487a400000 +5704004a78420000 5804004080847d00 5904700878502000 5a04781078502000 5b04047e14106000 5c04007822314800 +5d04784122107800 5e0418a1a2a27900 5f043c20e0203c00 6204027f4a483000 6304087e58502000 70040e107e100e00 +7104182078201800 72043c4a4a4a3c00 7304306858683000 7404001e70180c00 7504001860301000 9004007e02020300 +9104007808080c00 9204087e0a0a0200 9304207828080800 96043b043f043be0 970424183c1824c0 9a04003f040a31c0 +9b04003c101824c0 ae04060870080600 af040c10e0100c00 b004161870181600 b1042c30e0302c00 b20421120c1221c0 +b3040024181824c0 ba047e0808087000 bb04007e08087000 d804003452523c00 d904002868583000 e20400457d450000 +e304004a7a420000 e8043c4a4a4a3c00 e904003058583000 ee04003d41413d00 ef04003a42427a00 d005681020285000 +d105484848784000 d205004830600000 d305080808780800 d405680808087800 d505000008780000 d605080818680800 +d705087808087800 d805784050487800 d905000008180000 da0504040404fc00 db05484848483800 dc050e4848281800 +dd05087848487800 de05582010487000 df05000004fc0000 e005004040487800 e105000878483800 e205487840281800 +e305041c0404fc00 e405485848483800 e50504f820140800 e605485060685000 e705f40424241c00 e805080808087000 +e905785058403800 ea05487808087800 f005087800087800 f105081800087800 f205081800081800 f305000010080000 +f405100800100800 021e7c5455542800 031e007e48493000 0a1e007c45443800 0b1e003049487e00 1e1e007c15140400 +1f1e001079140800 401e7e0419047e00 411e780832087000 561e007c15140800 571e00fc25241800 601e004854552400 +611e0050586a2800 6a1e04047d040400 6b1e00083d482000 801e7c2112207c00 811e384132403800 821e7c2012217c00 +831e384032413800 841e7c2110217c00 851e384230423800 f21e040972080400 f31e00b84142f800 a3207e0a7a120a00 +a420a8fcaa828400 a720087e2a1c0800 ab200098a4a6bf02 ac20183c5a5a4200 af20627f22443800 9021103854101000 +912108047e040800 9221101054381000 932110207e201000 9421103810103810 95212844fe442800 +`, + + } + + // A unicode 12x12 pixel font based on https://github.com/adobe-fonts/source-han-sans + //% whenUsed jres + export const font12: Font = { + charWidth: 12, + charHeight: 12, + data: hex`` + } + + export function getFontForText(text: string) { + for (let i = 0; i < text.length; ++i) { + // this is quite approximate + if (text.charCodeAt(i) > 0x2000) + return simage.font12 + } + return simage.font8 + } + + //% deprecated=1 hidden=1 + export function doubledFont(f: Font): Font { + return scaledFont(f, 2) + } + + export function scaledFont(f: Font, size: number): Font { + size |= 0 + if (size < 2) + return f + return { + charWidth: f.charWidth * size, + charHeight: f.charHeight * size, + data: f.data, + multiplier: f.multiplier ? size * f.multiplier : size + } + } + + //% whenUsed + export const font5: Font = { + charWidth: 6, + charHeight: 5, + // source https://github.com/lancaster-university/microbit-dal/blob/master/source/core/MicroBitFont.cpp + data: hex` +2000000000000000 2100001700000000 2200000300030000 23000a1f0a1f0a00 24000a17151d0a00 2500130904121900 +26000a15150a1000 2700000300000000 2800000e11000000 290000110e000000 2a00000a040a0000 2b0000040e040000 +2c00001008000000 2d00000404040000 2e00000800000000 2f00100804020100 30000e11110e0000 310000121f100000 +3200191515120000 33000911150b0000 34000c0a091f0800 3500171515150900 3600081416150800 3700110905030100 +38000a1515150a00 390002150d050200 3a00000a00000000 3b0000100a000000 3c0000040a110000 3d00000a0a0a0000 +3e0000110a040000 3f00020115050200 40000e1115090e00 41001e05051e0000 42001f15150a0000 43000e1111110000 +44001f11110e0000 45001f1515110000 46001f0505010000 47000e1111150c00 48001f04041f0000 4900111f11000000 +4a000911110f0100 4b001f040a110000 4c001f1010100000 4d001f0204021f00 4e001f0204081f00 4f000e11110e0000 +50001f0505020000 5100060919160000 52001f05050a1000 5300121515090000 540001011f010100 55000f10100f0000 +5600070810080700 57001f0804081f00 58001b04041b0000 590001021c020100 5a00191513110000 5b00001f11110000 +5c00010204081000 5d000011111f0000 5e00000201020000 5f00101010101000 6000000102000000 61000c12121e1000 +62001f1414080000 63000c1212120000 64000814141f0000 65000e1515120000 6600041e05010000 67000215150f0000 +68001f0404180000 6900001d00000000 6a000010100d0000 6b001f040a100000 6c00000f10100000 6d001e0204021e00 +6e001e02021c0000 6f000c12120c0000 70001e0a0a040000 7100040a0a1e0000 72001c0202020000 730010140a020000 +7400000f14141000 75000e10101e1000 7600060810080600 77001e1008101e00 7800120c0c120000 7900121408040200 +7a00121a16120000 7b0000041f110000 7c00001f00000000 7d00111f04000000 7e00000404080800 d3000c1213130c00 +f3000c12130d0000 04010e05051e1000 05010609191f0800 06010c1213131200 07010c1213130000 18010f0b1b190000 +19010e151d1a0000 41011f1412100000 4201100f14120000 43011f0205081f00 44011e03031c0000 5a0110140b030200 +5b0110140b030000 7901121a17130000 7a01121a17130000 7b01121b17120000 7c01121b17120000`, + } +} + +namespace texteffects { + export interface TextEffectState { + xOffset: number; + yOffset: number; + } +} + +interface SImage { + //% helper=imagePrint + print(text: string, x: number, y: number, color?: number, font?: simage.Font, offsets?: texteffects.TextEffectState[]): void; + + //% helper=imagePrintCenter + printCenter(text: string, y: number, color?: number, font?: simage.Font): void; +} + +namespace helpers { + export function imagePrintCenter(img: SImage, text: string, y: number, color?: number, font?: simage.Font) { + if (!font) font = simage.getFontForText(text) + let w = text.length * font.charWidth + let x = (img.width - w) / 2 + imagePrint(img, text, x, y, color, font) + } + + export function imagePrint(img: SImage, text: string, x: number, y: number, color?: number, font?: simage.Font, offsets?: texteffects.TextEffectState[]) { + x |= 0 + y |= 0 + if (!font) + font = simage.getFontForText(text) + if (!color) color = 1 + let x0 = x + let cp = 0 + let mult = font.multiplier ? font.multiplier : 1 + let dataW = Math.idiv(font.charWidth, mult) + let dataH = Math.idiv(font.charHeight, mult) + let byteHeight = (dataH + 7) >> 3 + let charSize = byteHeight * dataW + let dataSize = 2 + charSize + let fontdata = font.data + let lastchar = Math.idiv(fontdata.length, dataSize) - 1 + let imgBuf: Buffer + if (mult == 1) { + imgBuf = control.createBuffer(8 + charSize) + imgBuf[0] = 0x87 + imgBuf[1] = 1 + imgBuf[2] = dataW + imgBuf[4] = dataH + } + while (cp < text.length) { + let xOffset = 0, yOffset = 0; + if (offsets && cp < offsets.length) { + xOffset = offsets[cp].xOffset + yOffset = offsets[cp].yOffset + } + + let ch = text.charCodeAt(cp++) + if (ch == 10) { + y += font.charHeight + 2 + x = x0 + } + + if (ch < 32) + continue // skip control chars + + let l = 0 + let r = lastchar + let off = 0 // this should be a space (0x0020) + let guess = (ch - 32) * dataSize + if (fontdata.getNumber(NumberFormat.UInt16LE, guess) == ch) + off = guess + else { + while (l <= r) { + let m = l + ((r - l) >> 1); + let v = fontdata.getNumber(NumberFormat.UInt16LE, m * dataSize) + if (v == ch) { + off = m * dataSize + break + } + if (v < ch) + l = m + 1 + else + r = m - 1 + } + } + + if (mult == 1) { + imgBuf.write(8, fontdata.slice(off + 2, charSize)) + img.drawIcon(imgBuf, x + xOffset, y + yOffset, color) + x += font.charWidth + } else { + off += 2 + for (let i = 0; i < dataW; ++i) { + let j = 0 + let mask = 0x01 + let c = fontdata[off++] + while (j < dataH) { + if (mask == 0x100) { + c = fontdata[off++] + mask = 0x01 + } + let n = 0 + while (c & mask) { + n++ + mask <<= 1 + } + if (n) { + img.fillRect(x + xOffset * mult, y + (j + yOffset) * mult, mult, mult * n, color) + j += n + } else { + mask <<= 1 + j++ + } + } + x += mult + } + } + } + } +} diff --git a/pxtarget.json b/pxtarget.json index 72ba554a5e3..d3b8a6c16b1 100644 --- a/pxtarget.json +++ b/pxtarget.json @@ -17,7 +17,8 @@ "libs/flashlog", "libs/datalogger", "libs/color", - "libs/audio-recording" + "libs/audio-recording", + "libs/arcadeshield" ], "cloud": { "workspace": false, @@ -174,10 +175,9 @@ "githubCorePackage": "lancaster-university/microbit", "gittag": "v2.2.0-rc6", "serviceId": "microbit", - "dockerImage": "pext/yotta:gcc5" + "dockerImage": "tball_yotta" }, "multiVariants": [ - "mbdal", "mbcodal" ], "alwaysMultiVariant" : true, @@ -196,8 +196,8 @@ "buildEngine": "codal", "codalTarget": { "name": "codal-microbit-v2", - "url": "https://github.com/lancaster-university/codal-microbit-v2", - "branch": "v0.2.63", + "url": "https://github.com/tballmsft/codal-microbit-v2", + "branch": "dmesgon", "type": "git" }, "codalBinary": "MICROBIT",