diff --git a/Adafruit_ESP32_Arduino_Demos/SerialESPPassthrough/SerialESPPassthrough.ino b/Adafruit_ESP32_Arduino_Demos/SerialESPPassthrough/SerialESPPassthrough.ino index a8e9d91bb..f6fd2a323 100644 --- a/Adafruit_ESP32_Arduino_Demos/SerialESPPassthrough/SerialESPPassthrough.ino +++ b/Adafruit_ESP32_Arduino_Demos/SerialESPPassthrough/SerialESPPassthrough.ino @@ -109,16 +109,32 @@ void setup() { digitalWrite(ESP32_RESETN, HIGH); pixel.setPixelColor(0, 20, 20, 0); pixel.show(); delay(100); + +#if defined(LED_BUILTIN) + pinMode(LED_BUILTIN, OUTPUT); +#endif } void loop() { while (Serial.available()) { +#if defined(ARDUINO_ARCH_RP2040) // Neopixel is blocking and this annoys esptool + #if defined(LED_BUILTIN) + digitalWrite(LED_BUILTIN, HIGH); + #endif +#else pixel.setPixelColor(0, 10, 0, 0); pixel.show(); +#endif SerialESP32.write(Serial.read()); } while (SerialESP32.available()) { +#if defined(ARDUINO_ARCH_RP2040) // Neopixel is blocking and this annoys esptool + #if defined(LED_BUILTIN) + digitalWrite(LED_BUILTIN, LOW); + #endif +#else pixel.setPixelColor(0, 0, 0, 10); pixel.show(); +#endif Serial.write(SerialESP32.read()); } } diff --git a/Adafruit_Feather_ESP32-S2/Arduino_BME280_DeepSleep_AdafruitIO/.feather_esp32s2.test.only b/Adafruit_Feather_ESP32-S2/Arduino_BME280_DeepSleep_AdafruitIO/.feather_esp32s2.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Adafruit_Feather_ESP32-S2/Arduino_BME280_DeepSleep_AdafruitIO/Arduino_BME280_DeepSleep_AdafruitIO.ino b/Adafruit_Feather_ESP32-S2/Arduino_BME280_DeepSleep_AdafruitIO/Arduino_BME280_DeepSleep_AdafruitIO.ino new file mode 100644 index 000000000..aab5557ac --- /dev/null +++ b/Adafruit_Feather_ESP32-S2/Arduino_BME280_DeepSleep_AdafruitIO/Arduino_BME280_DeepSleep_AdafruitIO.ino @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: 2025 Limor Fried for Adafruit Industries +// +// SPDX-License-Identifier: MIT +#include "config.h" +#include +#include + +Adafruit_BME280 bme; // I2C + +AdafruitIO_Feed *temperature = io.feed("temperature"); +AdafruitIO_Feed *humidity = io.feed("humidity"); +AdafruitIO_Feed *pressure = io.feed("pressure"); +float temp, humid, pres; + +Adafruit_NeoPixel pixel(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800); + + +void setup() { + Serial.begin(115200); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + + // wait for serial monitor to open + //while(! Serial); + + // turn on neopixel + pinMode(NEOPIXEL_POWER, OUTPUT); + digitalWrite(NEOPIXEL_POWER, HIGH); + pixel.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + pixel.setBrightness(10); // not so bright + + pixel.setPixelColor(0, 0xFF0000); // red + pixel.show(); + + if (! bme.begin()) { + Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!"); + deepSleep(); + } + Serial.println("Found BME280"); + float temp = bme.readTemperature(); + float pres = bme.readPressure() / 100.0F; + float hum = bme.readHumidity(); + // shhh time to close your eyes + bme.setSampling(Adafruit_BME280::MODE_SLEEP, + Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::SAMPLING_X16, Adafruit_BME280::SAMPLING_X16, + Adafruit_BME280::FILTER_OFF, + Adafruit_BME280::STANDBY_MS_1000); + + Serial.print("Connecting to Adafruit IO"); + + pixel.setPixelColor(0, 0xFFFF00); // yellow + pixel.show(); + + // connect to io.adafruit.com + io.connect(); + + // wait for a connection + while(io.status() < AIO_CONNECTED) { + Serial.print("."); + delay(100); + } + + // we are connected + pixel.setPixelColor(0, 0x00FF00); // green + pixel.show(); + Serial.println(); + Serial.println(io.statusText()); + + io.run(); + + temp = temp * 9.0 / 5.0 + 32; + Serial.print("Temperature = "); + Serial.print(temp); + Serial.println(" *F"); + temperature->save(temp); + + Serial.print("Pressure = "); + Serial.print(pres); + Serial.println(" hPa"); + pressure->save(pres); + + Serial.print("Humidity = "); + Serial.print(hum); + Serial.println(" %"); + humidity->save(hum); + + Serial.println(); + + deepSleep(); +} + +void loop() { + // we never get here! +} + + +void deepSleep() { + pinMode(NEOPIXEL_POWER, OUTPUT); + digitalWrite(NEOPIXEL_POWER, LOW); // off + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LOW); + + esp_sleep_enable_timer_wakeup(300000000); // 5 minutes + esp_deep_sleep_start(); +} \ No newline at end of file diff --git a/Adafruit_Feather_ESP32-S2/Arduino_BME280_DeepSleep_AdafruitIO/config.h b/Adafruit_Feather_ESP32-S2/Arduino_BME280_DeepSleep_AdafruitIO/config.h new file mode 100644 index 000000000..3585e138e --- /dev/null +++ b/Adafruit_Feather_ESP32-S2/Arduino_BME280_DeepSleep_AdafruitIO/config.h @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 Limor Fried for Adafruit Industries +// +// SPDX-License-Identifier: MIT +#define IO_USERNAME "your-aio-username" +#define IO_KEY "your-aio-token" +#define WIFI_SSID "your-wifi-ssid" +#define WIFI_PASS "your-wifi-pass" + +#include "AdafruitIO_WiFi.h" +AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS); \ No newline at end of file diff --git a/Adafruit_Feather_ESP32-S2/CircuitPython_BME280_DeepSleep_AdafruitIO/code.py b/Adafruit_Feather_ESP32-S2/CircuitPython_BME280_DeepSleep_AdafruitIO/code.py new file mode 100644 index 000000000..ffa8a970b --- /dev/null +++ b/Adafruit_Feather_ESP32-S2/CircuitPython_BME280_DeepSleep_AdafruitIO/code.py @@ -0,0 +1,110 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +CircuitPython example for deep sleep and BME280 sensor sending data +to Adafruit IO. +""" +from os import getenv +import time +import alarm +import board +import digitalio +import neopixel +import wifi + +from adafruit_bme280 import advanced as adafruit_bme280 +import adafruit_connection_manager +import adafruit_requests +from adafruit_io.adafruit_io import IO_HTTP, AdafruitIO_RequestError + + +# enable power to NeoPixels. +np_power = digitalio.DigitalInOut(board.NEOPIXEL_POWER) +np_power.switch_to_output(value=True) + +# standard LED +builtin_led = digitalio.DigitalInOut(board.LED) +builtin_led.switch_to_output(value=True) + +# neopixel to use for status +status_pixel = neopixel.NeoPixel( + board.NEOPIXEL, 1, brightness=0.1, pixel_order=neopixel.GRB, auto_write=True +) +status_pixel[0] = 0xFF0000 + +# Create sensor object, using the board's default I2C bus. +i2c = board.I2C() # uses board.SCL and board.SDA +bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) +print("Found BME280") +# change this to match the location's pressure (hPa) at sea level +bme280.sea_level_pressure = 1013.25 + +# temperature converted to F +temperature = bme280.temperature * 9 / 5 + 32 +humidity = bme280.relative_humidity +pressure = bme280.pressure +print("\nTemperature: %0.1f F" % temperature) +print("Humidity: %0.1f %%" % humidity) +print("Pressure: %0.1f hPa" % pressure) + +bme280.mode = adafruit_bme280.MODE_SLEEP +bme280.overscan_temperature = adafruit_bme280.OVERSCAN_X16 +bme280.overscan_humidity = adafruit_bme280.OVERSCAN_X16 +bme280.overscan_pressure = adafruit_bme280.OVERSCAN_X16 +bme280.iir_filter = adafruit_bme280.IIR_FILTER_DISABLE +bme280.standby_period = adafruit_bme280.STANDBY_TC_1000 + +# set status pixel to yellow +status_pixel[0] = 0xFFFF00 + +print("Connecting to AdafruitIO") + +# Get WiFi details and Adafruit IO keys, ensure these are setup in settings.toml +# (visit io.adafruit.com if you need to create an account, or if you need your Adafruit IO key.) +ssid = getenv("WIFI_SSID") +password = getenv("WIFI_PASSWORD") +aio_username = getenv("ADAFRUIT_AIO_USERNAME") +aio_key = getenv("ADAFRUIT_AIO_KEY") + +print("Connecting to %s" % ssid) +wifi.radio.connect(ssid, password) +print("Connected to %s!" % ssid) + +# setup socket pool and requests session +pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(wifi.radio) +requests = adafruit_requests.Session(pool, ssl_context) + +# Initialize an Adafruit IO HTTP API object +io = IO_HTTP(aio_username, aio_key, requests) + +# set status pixel to green +status_pixel[0] = 0x00FF00 + +try: + # Get the feeds from Adafruit IO + temperature_feed = io.get_feed("temperature") + humidity_feed = io.get_feed("humidity") + pressure_feed = io.get_feed("pressure") + + # send data to the feeds + io.send_data(temperature_feed["key"], temperature) + io.send_data(humidity_feed["key"], humidity) + io.send_data(pressure_feed["key"], pressure) + +except AdafruitIO_RequestError as e: + print(e) + print( + "You must create feeds on AdafruitIO for: temperature, humidity, and pressure" + ) + +# turn off the neopixel and builtin LED +np_power.value = False +builtin_led.value = False + +# Create an alarm that will trigger 5 minutes from now. +time_alarm = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + (5 * 60)) +# Exit the program, and then deep sleep until the alarm wakes us. +alarm.exit_and_deep_sleep_until_alarms(time_alarm) +# Does not return, so we never get here. diff --git a/Adafruit_I2S_BFF/CircuitPython/WAV/code.py b/Adafruit_I2S_BFF/CircuitPython/WAV/code.py index f6725b3c2..37b3dd42b 100644 --- a/Adafruit_I2S_BFF/CircuitPython/WAV/code.py +++ b/Adafruit_I2S_BFF/CircuitPython/WAV/code.py @@ -11,7 +11,7 @@ LOOP = False # Update to True loop WAV playback. False plays once. audio = audiobusio.I2SOut(board.A2, board.A1, board.A0) -with open("chikken.wav", "rb") as wave_file: +with open("booploop.wav", "rb") as wave_file: wav = audiocore.WaveFile(wave_file) print("Playing wav file!") diff --git a/Adafruit_IO_Reed_Switch/rpi-pico-w112213141.json b/Adafruit_IO_Reed_Switch/rpi-pico-w112213141.json new file mode 100644 index 000000000..03b261628 --- /dev/null +++ b/Adafruit_IO_Reed_Switch/rpi-pico-w112213141.json @@ -0,0 +1,27 @@ +{ + "exportVersion": "1.0.0", + "exportedBy": "tyeth_demo", + "exportedAt": "2025-05-02T17:08:03.857Z", + "exportedFromDevice": { + "board": "rpi-pico-w", + "firmwareVersion": "1.0.0-beta.100" + }, + "components": [ + { + "name": "Reed Switch", + "pinName": "D13", + "type": "reed_switch", + "mode": "DIGITAL", + "direction": "INPUT", + "period": 0, + "pull": "UP", + "isPin": true, + "visualization": { + "offLabel": "Open", + "offIcon": "fa6:solid:door-open", + "onLabel": "Closed", + "onIcon": "fa6:regular:door-closed" + } + } + ] +} \ No newline at end of file diff --git a/Adafruit_IO_Schedule_Trigger/code.py b/Adafruit_IO_Schedule_Trigger/code.py index 339a0f324..73b6dfd9f 100755 --- a/Adafruit_IO_Schedule_Trigger/code.py +++ b/Adafruit_IO_Schedule_Trigger/code.py @@ -98,11 +98,11 @@ def on_message(client, feed_id, payload): def on_relay_msg(client, topic, message): # Method called whenever user/feeds/relay has a new value - if message == "morning": - print("Morning - turning outlet ON") + if message == "1": + print("Received 1 - turning outlet ON") power_pin.value = True - elif message == "night": - print("Night - turning outlet OFF") + elif message == "0": + print("Received 0 - turning outlet OFF") power_pin.value = False else: print("Unexpected value received on relay feed.") diff --git a/Adafruit_IO_Template_Emails/rpi-pico-w-potty-training.json b/Adafruit_IO_Template_Emails/rpi-pico-w-potty-training.json new file mode 100644 index 000000000..8eec8ca1a --- /dev/null +++ b/Adafruit_IO_Template_Emails/rpi-pico-w-potty-training.json @@ -0,0 +1,83 @@ +{ + "exportVersion": "1.0.0", + "exportedBy": "tyeth", + "exportedAt": "2025-06-10T18:13:03.071Z", + "exportedFromDevice": { + "board": "rpi-pico-w", + "firmwareVersion": "1.0.0-beta.100" + }, + "components": [ + { + "name": "πŸ’¦ Wee Button", + "pinName": "D18", + "type": "push_button", + "mode": "DIGITAL", + "direction": "INPUT", + "period": 0, + "pull": "UP", + "isPin": true + }, + { + "name": "πŸ’¦ Wee LED", + "pinName": "D2", + "type": "led", + "mode": "DIGITAL", + "direction": "OUTPUT", + "isPin": true + }, + { + "name": "πŸ’© Poo Button", + "pinName": "D19", + "type": "push_button", + "mode": "DIGITAL", + "direction": "INPUT", + "period": 0, + "pull": "UP", + "isPin": true + }, + { + "name": "πŸ’© Poo LED", + "pinName": "D3", + "type": "led", + "mode": "DIGITAL", + "direction": "OUTPUT", + "isPin": true + }, + { + "name": "❌ Didn't Go Button", + "pinName": "D20", + "type": "push_button", + "mode": "DIGITAL", + "direction": "INPUT", + "period": 0, + "pull": "UP", + "isPin": true + }, + { + "name": "❌ Didn't Go LED", + "pinName": "D4", + "type": "led", + "mode": "DIGITAL", + "direction": "OUTPUT", + "isPin": true + }, + { + "name": "πŸ”” Tell Adult Button", + "pinName": "D21", + "type": "push_button", + "mode": "DIGITAL", + "direction": "INPUT", + "period": 0, + "pull": "UP", + "isPin": true + }, + { + "name": "πŸ”” Tell Adult LED", + "pinName": "D5", + "type": "led", + "mode": "DIGITAL", + "direction": "OUTPUT", + "isPin": true + } + ] +} \ No newline at end of file diff --git a/Adafruit_IO_Template_Emails/template.liquid b/Adafruit_IO_Template_Emails/template.liquid new file mode 100644 index 000000000..06e1fbb9c --- /dev/null +++ b/Adafruit_IO_Template_Emails/template.liquid @@ -0,0 +1,72 @@ +🌟 Daily Potty Training Report 🌟 +Hi there! Here's how our little superstar did: +--- +🚽 Successful Wees: {{ vars.wee_progress }} +{% for i in (1..vars.wee_progress) %}πŸ’§{% endfor %} +{% if vars.wee_progress <= 5 -%} +Great start! Every success counts, and they're building good habits one wee at a time! 🌱 +{% elsif vars.wee_progress <= 15 -%} +Fantastic progress! They're really getting the hang of this - keep up the amazing work! 🎯 +{% else -%} +SUPERSTAR ALERT! 🌟 Absolutely crushing it with those wee successes! They're a potty champion! πŸ† +{% endif %}--- + +πŸ’© Successful Poos: {{ vars.poo_progress }} +{% for i in (1..vars.poo_progress) %}🟀{% endfor %} +{% if vars.poo_progress == 0 -%} +Poos can be tricky, but they're being so brave! Every try is a step forward! πŸ’ͺ +{% elsif vars.poo_progress < 2 -%} +Look at them go! They're becoming a real poo pro - that's awesome progress! πŸŽ‰ +{% else -%} +POO CHAMPION! πŸ… They've mastered one of the trickiest parts - so proud! 🎊 +{% endif %}--- + +🀝 Told an Adult: {{ vars.informed_progress }} +{% for i in (1..vars.informed_progress) %}πŸ—£οΈ{% endfor %} +{% if vars.informed_progress <= 5 -%} +Communication is key! Keep practicing saying when they need to go - They're doing great! πŸ“’ +{% elsif vars.informed_progress <= 15 -%} +Wonderful communication skills! They're really good at letting us know - that's so helpful! πŸ‘ +{% else -%} +COMMUNICATION SUPERSTAR! 🌟 They're amazing at telling adults - that's such a big kid skill! 🎯 +{% endif %}--- + +πŸ‘» Nothing Happened: {{ vars.nothing_progress }} +{% for i in (1..vars.nothing_progress) %}β­•{% endfor %} +{% if vars.nothing_progress <= 3 -%} +That's okay! Trying is what matters, and their body will let them know when it's ready! 🌈 +{% else -%} +So patient and persistent! Even when nothing happens, they keep trying - that's real determination! πŸ’« +{% endif %}--- + +πŸ“Š Daily Summary: +{% capture total_tries -%}{{ vars.wee_progress | plus: vars.poo_progress | plus: vars.nothing_progress }}{% endcapture -%} +{% capture successes -%}{{ vars.wee_progress | plus: vars.poo_progress }}{% endcapture -%} +{% capture success_rate -%}{% if total_tries != "0" -%}{{ successes | times: 100 | divided_by: total_tries }}{% else -%}100{% endif -%}{% endcapture -%} +{% capture bar_filled -%}{{ success_rate | divided_by: 10 }}{% endcapture -%} +{% capture bar_empty -%}{{ 10 | minus: bar_filled }}{% endcapture -%} +Total potty visits: {{ total_tries }} +Success rate: {{ success_rate }}% [{%- for i in (1..bar_filled) -%}β–ˆ{%- endfor -%}{%- for i in (1..bar_empty) -%}β–‘{%- endfor -%}] +{%- assign total_events = vars.wee_progress | plus: vars.poo_progress | plus: vars.nothing_progress | plus: vars.informed_progress -%} + ({{ total_events }} events today ) +{% if total_events <= 3 %} +πŸ’ Today: They're doing such a great job learning! Every day gets a little easier... +{% elsif total_events <= 5 %} +🌟 Today: Ayee! They're really getting the hang of this potty training thing! Keep it up! +{% elsif total_events <= 8 %} +🌟 Today: WOW! Look at all that practice! They're becoming such a potty expert. +{% else %} +πŸ† Today: INCREDIBLE DAY! They're absolutely rocking this potty training journey! πŸŽŠπŸŽ‰ +{%- endif %} + +Keep being awesome! +With love and high-fives! πŸ™Œ +--- +{%- assign event_mod = total_events | modulo: 3 %} +{% if event_mod == 0 -%} +P.S. Remember: Every expert was once a beginner - they're doing brilliantly! 🌟 +{% elsif event_mod == 1 -%} +P.S. Fun fact: Even superheroes had to learn to use the potty! 🦸 +{% else -%} +P.S. Remember: accidents are just practice in disguise! They're doing amazingly! πŸ’• +{% endif %} diff --git a/Computer_Space/.feather_m4_express.test.only b/Computer_Space/.feather_m4_express.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Computer_Space/Computer_Space.ino b/Computer_Space/Computer_Space.ino new file mode 100644 index 000000000..6218dd724 --- /dev/null +++ b/Computer_Space/Computer_Space.ino @@ -0,0 +1,1120 @@ +// SPDX-FileCopyrightText: 2025 John Park and Claude +// +// SPDX-License-Identifier: MIT +// Computer Space simulation for Arduino + +// For Adafruit Feather M4 with OLED display + +#include +#include +#include +#include + +// Display setup +#define OLED_CS 5 +#define OLED_DC 6 +#define OLED_RESET 9 +Adafruit_SSD1305 display(128, 64, &SPI, OLED_DC, OLED_RESET, OLED_CS, 7000000UL); + +// Game constants +#define GAME_WIDTH 85 // Game area width - for 4:3 aspect ratio +#define GAME_HEIGHT 64 // Game area height +#define SCREEN_WIDTH 128 // Physical display width +#define SCREEN_HEIGHT 64 // Physical display height +#define GAME_X_OFFSET ((SCREEN_WIDTH - GAME_WIDTH) / 2) // Center the game area +#define GAME_Y_OFFSET 0 +#define WHITE 1 +#define BLACK 0 + +// Score positions - all inside game area +#define SCORE_Y_TOP 18 // Player score +#define SCORE_Y_MIDDLE 32 // Saucer score +#define SCORE_Y_BOTTOM 46 // Timer + +// Score fonts are 3x5 pixels +#define DIGIT_WIDTH 3 +#define DIGIT_HEIGHT 5 +#define DIGIT_SPACING 5 // Total width including spacing + +// Scores +int player_score = 0; // Starting scores at 0 as requested +int saucer_score = 0; +unsigned long game_timer = 0; // Starting from 00 and counting up to 99 +const unsigned long GAME_DURATION = 99000; // 99 seconds + +// Number of stars (similar to PDP-1 Spacewar!) +#define NUM_STARS 40 + +// Ship and saucer states +#define ALIVE 0 +#define EXPLODING 1 +#define RESPAWNING 2 +#define EXPLOSION_FRAMES 12 // Number of frames for explosion animation + +// Screen flash effect +bool screen_flash = false; +int flash_frames = 0; +#define FLASH_DURATION 3 // How long to flash the screen + +// Game objects +// Ship +float ship_x, ship_y; +float ship_vx, ship_vy; +float ship_rotation = 0; +float target_rotation = 0; +const float ship_thrust = 0.13; // 33% slower than original 0.2 +bool ship_thrusting = false; +int ship_state = ALIVE; +int ship_explosion_frame = 0; +unsigned long ship_respawn_time = 0; + +// Saucers (moving in formation) +float saucer1_x, saucer1_y; +float saucer2_x, saucer2_y; +float saucer_vertical_distance; // Distance between saucers (maintained) +unsigned long direction_change_time = 0; +int saucer1_state = ALIVE; +int saucer2_state = ALIVE; +int saucer1_explosion_frame = 0; +int saucer2_explosion_frame = 0; +unsigned long saucer1_respawn_time = 0; +unsigned long saucer2_respawn_time = 0; + +// Diagonal movement table +const int8_t MOVEMENT_TABLE[][2] = { + { 1, 0}, // Right + {-1, 0}, // Left + { 0, -1}, // Up + { 0, 1}, // Down + { 1, 1}, // Down-Right + {-1, -1}, // Up-Left + {-1, 1}, // Down-Left + { 1, -1} // Up-Right +}; +uint8_t current_movement = 0; + +// Bullets +bool player_bullet_active = false; +float player_bullet_x, player_bullet_y; +float player_bullet_vx, player_bullet_vy; +unsigned long player_bullet_expire = 0; +float player_bullet_tracking_factor = 0.08; // How much the bullet tracks ship rotation + +bool saucer1_bullet_active = false; +float saucer1_bullet_x, saucer1_bullet_y; +float saucer1_bullet_vx, saucer1_bullet_vy; +unsigned long saucer1_bullet_expire = 0; + +bool saucer2_bullet_active = false; +float saucer2_bullet_x, saucer2_bullet_y; +float saucer2_bullet_vx, saucer2_bullet_vy; +unsigned long saucer2_bullet_expire = 0; + +// Shooting cooldowns +unsigned long player_fire_cooldown = 0; +unsigned long saucer_fire_cooldown = 0; +const unsigned long PLAYER_COOLDOWN = 700; // milliseconds +const unsigned long SAUCER_COOLDOWN = 1500; // milliseconds +const unsigned long BULLET_LIFETIME = 2000; // 2 seconds as requested +const float BULLET_SPEED = 42.0; // Much faster than ship movement + +// Respawn timing +const unsigned long RESPAWN_DELAY = 2000; // 2 seconds + +// Game timing +unsigned long last_time = 0; +unsigned long auto_rotation_time = 0; +unsigned long auto_thrust_time = 0; +unsigned long game_start_time = 0; +unsigned long timer_update_time = 0; + +// Star coordinates +uint8_t stars[NUM_STARS][2]; + +// Background counter for star flicker (PDP-1 style) +uint8_t bg_counter = 0; + +// Digit patterns for 0-9 (3x5 pixels, 1 for pixel on, 0 for pixel off) +const uint8_t DIGITS[10][DIGIT_HEIGHT][DIGIT_WIDTH] = { + // 0 + { + {1, 1, 1}, + {1, 0, 1}, + {1, 0, 1}, + {1, 0, 1}, + {1, 1, 1} + }, + // 1 + { + {0, 1, 0}, + {0, 1, 0}, + {0, 1, 0}, + {0, 1, 0}, + {0, 1, 0} + }, + // 2 + { + {1, 1, 1}, + {0, 0, 1}, + {1, 1, 1}, + {1, 0, 0}, + {1, 1, 1} + }, + // 3 + { + {1, 1, 1}, + {0, 0, 1}, + {1, 1, 1}, + {0, 0, 1}, + {1, 1, 1} + }, + // 4 + { + {1, 0, 1}, + {1, 0, 1}, + {1, 1, 1}, + {0, 0, 1}, + {0, 0, 1} + }, + // 5 + { + {1, 1, 1}, + {1, 0, 0}, + {1, 1, 1}, + {0, 0, 1}, + {1, 1, 1} + }, + // 6 + { + {1, 1, 1}, + {1, 0, 0}, + {1, 1, 1}, + {1, 0, 1}, + {1, 1, 1} + }, + // 7 + { + {1, 1, 1}, + {0, 0, 1}, + {0, 0, 1}, + {0, 0, 1}, + {0, 0, 1} + }, + // 8 + { + {1, 1, 1}, + {1, 0, 1}, + {1, 1, 1}, + {1, 0, 1}, + {1, 1, 1} + }, + // 9 + { + {1, 1, 1}, + {1, 0, 1}, + {1, 1, 1}, + {0, 0, 1}, + {1, 1, 1} + } +}; + +// Explosion pattern data - expanding circle animation +const uint8_t EXPLOSION_RADIUS[] = {1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1}; +const uint8_t EXPLOSION_POINTS = 8; // Points to draw in the circle + +// Function prototypes +void drawDigit(int digit, int x, int y); +void drawScore(int score, int x, int y); +void drawScores(); +void clearScoreArea(); +void drawStars(); +void clearShip(); +void clearSaucer(float x, float y); +bool checkCollision(float x1, float y1, float x2, float y2, float radius); +bool isAimedAtSaucer(float ship_x, float ship_y, float ship_rotation, float saucer_x, float saucer_y, float tolerance = 0.6); +void drawShip(float x, float y, float rotation); +void drawSaucer(float x, float y); +void updateSaucerBullet(bool &active, float &x, float &y, float &vx, float &vy, unsigned long &expire, float dt, unsigned long current_time); +float normalizeAngle(float angle); +float getAngleDifference(float a1, float a2); +float getAngleToTarget(float x1, float y1, float x2, float y2); +void updateSaucers(float dt, unsigned long current_time); +void updateTimer(); +void drawExplosion(float x, float y, int frame); +void respawnShip(); +void respawnSaucer(int saucer_num); +void triggerScreenFlash(); + +void setup() { + Serial.begin(9600); + + // Initialize display + if (!display.begin()) { + Serial.println("SSD1305 allocation failed"); + while (1); + } + + // Show intro text + display.clearDisplay(); + display.setTextSize(1); + display.setTextColor(WHITE); + display.setCursor(10, 10); + display.println("Computer Space"); + display.setCursor(32, 30); + display.println("PDP-1 Style"); + display.setCursor(32, 50); + display.println("Demo Mode"); + display.display(); + delay(2000); + + display.clearDisplay(); + + // Initialize stars (PDP-1 style - fewer stars) + randomSeed(analogRead(0)); + for (int i = 0; i < NUM_STARS; i++) { + stars[i][0] = GAME_X_OFFSET + random(0, GAME_WIDTH); + stars[i][1] = GAME_Y_OFFSET + random(0, GAME_HEIGHT); + } + + // Initialize game objects + respawnShip(); + + // Initialize saucers in formation (one above the other) + respawnSaucer(1); + respawnSaucer(2); + saucer_vertical_distance = saucer2_y - saucer1_y; + + direction_change_time = millis() + random(2000, 5000); + game_start_time = millis(); + timer_update_time = millis(); + + // Draw the stars and initial score display + drawStars(); + drawScores(); + display.display(); + + last_time = millis(); +} + +void loop() { + unsigned long current_time = millis(); + float dt = (current_time - last_time) / 1000.0; // Convert to seconds + last_time = current_time; + + // Cap dt to prevent large jumps + if (dt > 0.1) dt = 0.1; + + // Update timer + updateTimer(); + + // Handle screen flash effect + if (screen_flash) { + // Flash the entire screen white + if (flash_frames == 0) { + display.fillRect(GAME_X_OFFSET, GAME_Y_OFFSET, GAME_WIDTH, GAME_HEIGHT, WHITE); + display.display(); + delay(50); // Short delay to make flash visible + } + + flash_frames++; + if (flash_frames >= FLASH_DURATION) { + screen_flash = false; + flash_frames = 0; + // Clear screen after flash + display.fillRect(GAME_X_OFFSET, GAME_Y_OFFSET, GAME_WIDTH, GAME_HEIGHT, BLACK); + } + } + + // Clear previous objects + clearShip(); + clearSaucer(saucer1_x, saucer1_y); + clearSaucer(saucer2_x, saucer2_y); + + if (player_bullet_active) { + display.drawPixel(player_bullet_x, player_bullet_y, BLACK); + } + if (saucer1_bullet_active) { + display.drawPixel(saucer1_bullet_x, saucer1_bullet_y, BLACK); + } + if (saucer2_bullet_active) { + display.drawPixel(saucer2_bullet_x, saucer2_bullet_y, BLACK); + } + + // Process ship based on its state + if (ship_state == ALIVE) { + // Simulate AI decisions for the player ship (PDP-1 style) + if (current_time > auto_rotation_time) { + // Choose a target to aim at (50% chance for each saucer if they're alive) + if (saucer1_state == ALIVE && saucer2_state == ALIVE) { + if (random(100) > 50) { + target_rotation = getAngleToTarget(ship_x, ship_y, saucer1_x, saucer1_y); + } else { + target_rotation = getAngleToTarget(ship_x, ship_y, saucer2_x, saucer2_y); + } + } else if (saucer1_state == ALIVE) { + target_rotation = getAngleToTarget(ship_x, ship_y, saucer1_x, saucer1_y); + } else if (saucer2_state == ALIVE) { + target_rotation = getAngleToTarget(ship_x, ship_y, saucer2_x, saucer2_y); + } else { + // Both saucers exploding/respawning, just pick a random direction + target_rotation = random(0, 628) / 100.0; // Random angle between 0 and 2*PI + } + + // Add some randomness to make it less precise (PDP-1 style) + target_rotation += random(-30, 30) * 0.01; + target_rotation = normalizeAngle(target_rotation); + + auto_rotation_time = current_time + random(1000, 3000); + } + + if (current_time > auto_thrust_time) { + // Thrusting decision based on aim + float angle_diff = abs(getAngleDifference(ship_rotation, target_rotation)); + if (angle_diff < 0.2) { + // If aimed correctly, thrust for a while + ship_thrusting = true; + auto_thrust_time = current_time + random(300, 800); + } else { + // If not aimed correctly, don't thrust + ship_thrusting = false; + auto_thrust_time = current_time + random(100, 300); + } + } + + // Update ship rotation with smoother movement (PDP-1 style) + float angle_diff = getAngleDifference(ship_rotation, target_rotation); + if (abs(angle_diff) > 0.05) { + // Rotate at a maximum of 0.05 radians per frame for smoother movement + ship_rotation += (angle_diff > 0 ? 1 : -1) * min(abs(angle_diff), 0.05f); + ship_rotation = normalizeAngle(ship_rotation); + } + + // Apply thrust to ship ONLY in the direction it's pointing + if (ship_thrusting) { + // Since the rocket points up (negative y) when rotation=0, + // we need to adjust the thrust direction by 90 degrees + ship_vx += cos(ship_rotation - PI/2) * ship_thrust; + ship_vy += sin(ship_rotation - PI/2) * ship_thrust; + } + + // Update position based on velocity (inertia - PDP-1 style physics) + ship_x += ship_vx * dt; + ship_y += ship_vy * dt; + + // Apply very slight drag (just to prevent perpetual motion) + ship_vx *= 0.995; + ship_vy *= 0.995; + + // Wrap ship around game area + if (ship_x < GAME_X_OFFSET) { + ship_x = GAME_X_OFFSET + GAME_WIDTH - 1; + } else if (ship_x >= GAME_X_OFFSET + GAME_WIDTH) { + ship_x = GAME_X_OFFSET; + } + + if (ship_y < GAME_Y_OFFSET) { + ship_y = GAME_Y_OFFSET + GAME_HEIGHT - 1; + } else if (ship_y >= GAME_Y_OFFSET + GAME_HEIGHT) { + ship_y = GAME_Y_OFFSET; + } + + // Check for collision with saucers + if (saucer1_state == ALIVE && checkCollision(ship_x, ship_y, saucer1_x, saucer1_y, 8)) { + // Collided with saucer 1 + ship_state = EXPLODING; + ship_explosion_frame = 0; + saucer1_state = EXPLODING; + saucer1_explosion_frame = 0; + + // Increment saucer score + saucer_score++; + if (saucer_score > 9) saucer_score = 9; // Cap at 9 + + // Trigger screen flash + triggerScreenFlash(); + } + else if (saucer2_state == ALIVE && checkCollision(ship_x, ship_y, saucer2_x, saucer2_y, 8)) { + // Collided with saucer 2 + ship_state = EXPLODING; + ship_explosion_frame = 0; + saucer2_state = EXPLODING; + saucer2_explosion_frame = 0; + + // Increment saucer score + saucer_score++; + if (saucer_score > 9) saucer_score = 9; // Cap at 9 + + // Trigger screen flash + triggerScreenFlash(); + } + + // Player shooting - only when aimed at a saucer + if (!player_bullet_active && current_time > player_fire_cooldown && random(100) > 90) { + // Check if aimed at any saucer + bool can_fire = false; + + if (saucer1_state == ALIVE && isAimedAtSaucer(ship_x, ship_y, ship_rotation, saucer1_x, saucer1_y)) { + can_fire = true; + } else if (saucer2_state == ALIVE && isAimedAtSaucer(ship_x, ship_y, ship_rotation, saucer2_x, saucer2_y)) { + can_fire = true; + } + + if (can_fire) { + player_bullet_active = true; + player_bullet_x = ship_x + cos(ship_rotation - PI/2) * 6; + player_bullet_y = ship_y + sin(ship_rotation - PI/2) * 6; + player_bullet_vx = cos(ship_rotation - PI/2) * BULLET_SPEED; + player_bullet_vy = sin(ship_rotation - PI/2) * BULLET_SPEED; + player_bullet_expire = current_time + BULLET_LIFETIME; + player_fire_cooldown = current_time + PLAYER_COOLDOWN; + } + } + } + else if (ship_state == EXPLODING) { + // Ship is exploding, update animation + ship_explosion_frame++; + if (ship_explosion_frame >= EXPLOSION_FRAMES) { + ship_state = RESPAWNING; + ship_respawn_time = current_time + RESPAWN_DELAY; + } + } + else if (ship_state == RESPAWNING) { + // Check if it's time to respawn + if (current_time > ship_respawn_time) { + respawnShip(); + } + } + + // Update saucers based on state + if (saucer1_state == ALIVE && saucer2_state == ALIVE) { + // Update saucers (in formation, PDP-1 style) + updateSaucers(dt, current_time); + } + else if (saucer1_state == EXPLODING) { + // Saucer 1 is exploding, update animation + saucer1_explosion_frame++; + if (saucer1_explosion_frame >= EXPLOSION_FRAMES) { + saucer1_state = RESPAWNING; + saucer1_respawn_time = current_time + RESPAWN_DELAY; + } + } + else if (saucer1_state == RESPAWNING) { + // Check if it's time to respawn saucer 1 + if (current_time > saucer1_respawn_time) { + respawnSaucer(1); + + // If saucer 2 is also active, update the formation distance + if (saucer2_state == ALIVE) { + saucer_vertical_distance = saucer2_y - saucer1_y; + } + } + } + + if (saucer2_state == EXPLODING) { + // Saucer 2 is exploding, update animation + saucer2_explosion_frame++; + if (saucer2_explosion_frame >= EXPLOSION_FRAMES) { + saucer2_state = RESPAWNING; + saucer2_respawn_time = current_time + RESPAWN_DELAY; + } + } + else if (saucer2_state == RESPAWNING) { + // Check if it's time to respawn saucer 2 + if (current_time > saucer2_respawn_time) { + respawnSaucer(2); + + // If saucer 1 is also active, update the formation distance + if (saucer1_state == ALIVE) { + saucer_vertical_distance = saucer2_y - saucer1_y; + } + } + } + + // Update player bullet with tracking behavior + if (player_bullet_active) { + // If ship is alive, update bullet direction to track ship's rotation + if (ship_state == ALIVE) { + // Calculate the target velocity based on current ship rotation + float target_vx = cos(ship_rotation - PI/2) * BULLET_SPEED; + float target_vy = sin(ship_rotation - PI/2) * BULLET_SPEED; + + // Gradually adjust bullet velocity to track the ship's rotation + player_bullet_vx += (target_vx - player_bullet_vx) * player_bullet_tracking_factor; + player_bullet_vy += (target_vy - player_bullet_vy) * player_bullet_tracking_factor; + + // Normalize velocity to maintain constant speed + float speed = sqrt(player_bullet_vx * player_bullet_vx + player_bullet_vy * player_bullet_vy); + if (speed > 0) { + player_bullet_vx = (player_bullet_vx / speed) * BULLET_SPEED; + player_bullet_vy = (player_bullet_vy / speed) * BULLET_SPEED; + } + } + + // Update position + player_bullet_x += player_bullet_vx * dt; + player_bullet_y += player_bullet_vy * dt; + + // Wrap bullet within game area + if (player_bullet_x < GAME_X_OFFSET) { + player_bullet_x = GAME_X_OFFSET + GAME_WIDTH - 1; + } else if (player_bullet_x >= GAME_X_OFFSET + GAME_WIDTH) { + player_bullet_x = GAME_X_OFFSET; + } + + if (player_bullet_y < GAME_Y_OFFSET) { + player_bullet_y = GAME_Y_OFFSET + GAME_HEIGHT - 1; + } else if (player_bullet_y >= GAME_Y_OFFSET + GAME_HEIGHT) { + player_bullet_y = GAME_Y_OFFSET; + } + + // Check collisions with saucers + if (saucer1_state == ALIVE && checkCollision(player_bullet_x, player_bullet_y, saucer1_x, saucer1_y, 4)) { + player_bullet_active = false; + saucer1_state = EXPLODING; + saucer1_explosion_frame = 0; + + // Increment player score + player_score++; + if (player_score > 9) player_score = 9; // Cap at 9 + + // Trigger screen flash + triggerScreenFlash(); + } + else if (saucer2_state == ALIVE && checkCollision(player_bullet_x, player_bullet_y, saucer2_x, saucer2_y, 4)) { + player_bullet_active = false; + saucer2_state = EXPLODING; + saucer2_explosion_frame = 0; + + // Increment player score + player_score++; + if (player_score > 9) player_score = 9; // Cap at 9 + + // Trigger screen flash + triggerScreenFlash(); + } + + // Bullet lifetime + if (current_time > player_bullet_expire) { + player_bullet_active = false; + } + } + + // Update saucer bullets + if (saucer1_state == ALIVE) { + updateSaucerBullet(saucer1_bullet_active, saucer1_bullet_x, saucer1_bullet_y, + saucer1_bullet_vx, saucer1_bullet_vy, saucer1_bullet_expire, dt, current_time); + } + + if (saucer2_state == ALIVE) { + updateSaucerBullet(saucer2_bullet_active, saucer2_bullet_x, saucer2_bullet_y, + saucer2_bullet_vx, saucer2_bullet_vy, saucer2_bullet_expire, dt, current_time); + } + + // Saucer shooting (PDP-1 style random timing) + if (!saucer1_bullet_active && !saucer2_bullet_active && current_time > saucer_fire_cooldown) { + if (random(100) > 50 && saucer1_state == ALIVE && ship_state == ALIVE) { + // Saucer 1 shoots + saucer1_bullet_active = true; + saucer1_bullet_x = saucer1_x; + saucer1_bullet_y = saucer1_y; + + // Aim towards player with some randomness (PDP-1 style accuracy) + float angle = atan2(ship_y - saucer1_y, ship_x - saucer1_x) + + (random(-50, 50) / 100.0); + saucer1_bullet_vx = cos(angle) * BULLET_SPEED * 0.7; + saucer1_bullet_vy = sin(angle) * BULLET_SPEED * 0.7; + saucer1_bullet_expire = current_time + BULLET_LIFETIME; + } else if (saucer2_state == ALIVE && ship_state == ALIVE) { + // Saucer 2 shoots + saucer2_bullet_active = true; + saucer2_bullet_x = saucer2_x; + saucer2_bullet_y = saucer2_y; + + // Aim towards player with some randomness + float angle = atan2(ship_y - saucer2_y, ship_x - saucer2_x) + + (random(-50, 50) / 100.0); + saucer2_bullet_vx = cos(angle) * BULLET_SPEED * 0.7; + saucer2_bullet_vy = sin(angle) * BULLET_SPEED * 0.7; + saucer2_bullet_expire = current_time + BULLET_LIFETIME; + } + saucer_fire_cooldown = current_time + SAUCER_COOLDOWN; + } + + // Update background counter for star flicker effect (PDP-1 style) + bg_counter++; + + // Draw stars only on certain frames (PDP-1 style) + if (bg_counter % 2 == 0) { + drawStars(); + } + + // Only draw game objects if not in a screen flash + if (!screen_flash) { + // Draw game objects based on their state + if (ship_state == ALIVE) { + drawShip(ship_x, ship_y, ship_rotation); + } else if (ship_state == EXPLODING) { + drawExplosion(ship_x, ship_y, ship_explosion_frame); + } + + if (saucer1_state == ALIVE) { + drawSaucer(saucer1_x, saucer1_y); + } else if (saucer1_state == EXPLODING) { + drawExplosion(saucer1_x, saucer1_y, saucer1_explosion_frame); + } + + if (saucer2_state == ALIVE) { + drawSaucer(saucer2_x, saucer2_y); + } else if (saucer2_state == EXPLODING) { + drawExplosion(saucer2_x, saucer2_y, saucer2_explosion_frame); + } + + // Draw bullets + if (player_bullet_active) { + display.drawPixel(player_bullet_x, player_bullet_y, WHITE); + } + if (saucer1_bullet_active) { + display.drawPixel(saucer1_bullet_x, saucer1_bullet_y, WHITE); + } + if (saucer2_bullet_active) { + display.drawPixel(saucer2_bullet_x, saucer2_bullet_y, WHITE); + } + + // Clear score area and draw scores + clearScoreArea(); + drawScores(); + } + + // Update display + display.display(); + + // Small delay for performance + delay(20); // ~50 FPS - Similar to PDP-1 refresh rate +} + +// Trigger a white screen flash effect +void triggerScreenFlash() { + screen_flash = true; + flash_frames = 0; +} + +// Draw a PDP-1 style explosion animation +void drawExplosion(float x, float y, int frame) { + int radius = EXPLOSION_RADIUS[frame]; + + // Draw expanding circle with points + for (int i = 0; i < EXPLOSION_POINTS; i++) { + float angle = i * (2.0 * PI / EXPLOSION_POINTS); + int px = x + radius * cos(angle); + int py = y + radius * sin(angle); + + // Only draw if within game area + if (px >= GAME_X_OFFSET && px < GAME_X_OFFSET + GAME_WIDTH && + py >= GAME_Y_OFFSET && py < GAME_Y_OFFSET + GAME_HEIGHT) { + display.drawPixel(px, py, WHITE); + } + + // Add some randomly placed debris particles + if (frame > 2 && frame < 10) { + float debris_angle = angle + random(-30, 30) * 0.01; + float debris_dist = random(1, radius + 2); + int dx = x + debris_dist * cos(debris_angle); + int dy = y + debris_dist * sin(debris_angle); + + if (dx >= GAME_X_OFFSET && dx < GAME_X_OFFSET + GAME_WIDTH && + dy >= GAME_Y_OFFSET && dy < GAME_Y_OFFSET + GAME_HEIGHT) { + display.drawPixel(dx, dy, WHITE); + } + } + } +} + +// Respawn ship at a random position +void respawnShip() { + ship_x = GAME_X_OFFSET + random(GAME_WIDTH/4, 3*GAME_WIDTH/4); + ship_y = GAME_Y_OFFSET + random(GAME_HEIGHT/4, 3*GAME_HEIGHT/4); + ship_vx = ship_vy = 0; + ship_state = ALIVE; +} + +// Respawn saucer at a new position +void respawnSaucer(int saucer_num) { + if (saucer_num == 1) { + saucer1_x = GAME_X_OFFSET + random(10, GAME_WIDTH - 10); + saucer1_y = GAME_Y_OFFSET + random(10, GAME_HEIGHT/2 - 10); + saucer1_state = ALIVE; + } else { + saucer2_x = GAME_X_OFFSET + random(10, GAME_WIDTH - 10); + saucer2_y = GAME_Y_OFFSET + random(GAME_HEIGHT/2 + 10, GAME_HEIGHT - 10); + saucer2_state = ALIVE; + } +} + +// Draw a single digit using our custom font +void drawDigit(int digit, int x, int y) { + if (digit < 0 || digit > 9) return; // Only support 0-9 + + // Draw the digit pixel by pixel + for (int row = 0; row < DIGIT_HEIGHT; row++) { + for (int col = 0; col < DIGIT_WIDTH; col++) { + if (DIGITS[digit][row][col] == 1) { + display.drawPixel(x + col, y + row, WHITE); + } + } + } +} + +// Draw a score with multiple digits +void drawScore(int score, int x, int y) { + // Handle single and double digit scores + if (score < 10) { + // Single digit - add leading zero for timer + if (y == SCORE_Y_BOTTOM) { + // This is the timer, draw leading zero + drawDigit(0, x - DIGIT_SPACING, y); + drawDigit(score, x, y); + } else { + // Single digit score + drawDigit(score, x, y); + } + } else if (score < 100) { + // Double digits + int tens = score / 10; + int ones = score % 10; + drawDigit(tens, x - DIGIT_SPACING, y); + drawDigit(ones, x, y); + } +} + +// Update game timer - always counts up from 00 to 99 +void updateTimer() { + unsigned long current_time = millis(); + + // Update timer only once per second + if (current_time - timer_update_time >= 1000) { + timer_update_time = current_time; + + // Always increment timer + game_timer++; + if (game_timer >= 100) { + game_timer = 0; // Reset to 00 when reaching 100 + } + } +} + +// Clear the score area more thoroughly +void clearScoreArea() { + // Define score display areas + for (int i = 0; i < 3; i++) { + int y_pos; + switch (i) { + case 0: y_pos = SCORE_Y_TOP; break; + case 1: y_pos = SCORE_Y_MIDDLE; break; + case 2: y_pos = SCORE_Y_BOTTOM; break; + } + + // Clear area for double-digit score (including spacing) + for (int y = y_pos; y < y_pos + DIGIT_HEIGHT; y++) { + for (int x = GAME_X_OFFSET + GAME_WIDTH - 12; x < GAME_X_OFFSET + GAME_WIDTH; x++) { + display.drawPixel(x, y, BLACK); + } + } + } +} + +// Draw scores with custom font, positioned at edge of game area +void drawScores() { + // Position scores a few pixels from the right edge of game area + int score_x = GAME_X_OFFSET + GAME_WIDTH - 5; + + // Player score at top position + drawScore(player_score, score_x, SCORE_Y_TOP); + + // Saucer score at middle position + drawScore(saucer_score, score_x, SCORE_Y_MIDDLE); + + // Timer at bottom position (always show as 2 digits) + drawScore(game_timer, score_x, SCORE_Y_BOTTOM); +} + +// Update saucers using the PDP-1 style zig-zag formation movement +void updateSaucers(float dt, unsigned long current_time) { + // Check if it's time to change direction + if (current_time > direction_change_time) { + // Select a new movement pattern from the table + current_movement = random(0, 8); // 8 possible movement directions + + // Set next direction change time + direction_change_time = current_time + random(1500, 3000); + } + + // Apply current movement pattern + float speed_factor = 25.0 * dt; + float dx = MOVEMENT_TABLE[current_movement][0] * speed_factor; + float dy = MOVEMENT_TABLE[current_movement][1] * speed_factor; + + // Update saucer positions, maintaining their formation + saucer1_x += dx; + saucer1_y += dy; + + // Always keep saucer2 at the same position relative to saucer1 + saucer2_x = saucer1_x; + saucer2_y = saucer1_y + saucer_vertical_distance; + + // Wrap saucers around the game area + if (saucer1_x < GAME_X_OFFSET) { + saucer1_x = GAME_X_OFFSET + GAME_WIDTH - 1; + saucer2_x = saucer1_x; + } else if (saucer1_x >= GAME_X_OFFSET + GAME_WIDTH) { + saucer1_x = GAME_X_OFFSET; + saucer2_x = saucer1_x; + } + + if (saucer1_y < GAME_Y_OFFSET) { + saucer1_y = GAME_Y_OFFSET + GAME_HEIGHT - 1; + saucer2_y = saucer1_y + saucer_vertical_distance; + } else if (saucer1_y >= GAME_Y_OFFSET + GAME_HEIGHT) { + saucer1_y = GAME_Y_OFFSET; + saucer2_y = saucer1_y + saucer_vertical_distance; + } + + // Also wrap saucer2 if it goes off screen + if (saucer2_y >= GAME_Y_OFFSET + GAME_HEIGHT) { + saucer2_y = GAME_Y_OFFSET + (saucer2_y - (GAME_Y_OFFSET + GAME_HEIGHT)); + saucer1_y = saucer2_y - saucer_vertical_distance; + } else if (saucer2_y < GAME_Y_OFFSET) { + saucer2_y = GAME_Y_OFFSET + GAME_HEIGHT - (GAME_Y_OFFSET - saucer2_y); + saucer1_y = saucer2_y - saucer_vertical_distance; + } +} + +// Normalize angle to [0, 2Ο€] +float normalizeAngle(float angle) { + while (angle < 0) angle += 2 * PI; + while (angle >= 2 * PI) angle -= 2 * PI; + return angle; +} + +// Get the shortest angle difference between two angles +float getAngleDifference(float a1, float a2) { + float diff = normalizeAngle(a2 - a1); + if (diff > PI) diff -= 2 * PI; + return diff; +} + +// Get angle from point 1 to point 2 +float getAngleToTarget(float x1, float y1, float x2, float y2) { + return atan2(y2 - y1, x2 - x1); +} + +// Helper function to update saucer bullets +void updateSaucerBullet(bool &active, float &x, float &y, float &vx, float &vy, + unsigned long &expire, float dt, unsigned long current_time) { + if (active) { + x += vx * dt; + y += vy * dt; + + // Wrap bullet within game area + if (x < GAME_X_OFFSET) { + x = GAME_X_OFFSET + GAME_WIDTH - 1; + } else if (x >= GAME_X_OFFSET + GAME_WIDTH) { + x = GAME_X_OFFSET; + } + + if (y < GAME_Y_OFFSET) { + y = GAME_Y_OFFSET + GAME_HEIGHT - 1; + } else if (y >= GAME_Y_OFFSET + GAME_HEIGHT) { + y = GAME_Y_OFFSET; + } + + // Check collision with player + if (ship_state == ALIVE && checkCollision(x, y, ship_x, ship_y, 4)) { + active = false; + ship_state = EXPLODING; + ship_explosion_frame = 0; + + // Increment saucer score + saucer_score++; + if (saucer_score > 9) saucer_score = 9; // Cap at 9 + + // Trigger screen flash + triggerScreenFlash(); + } + + // Bullet lifetime + if (current_time > expire) { + active = false; + } + } +} + +// Draw stars +void drawStars() { + for (int i = 0; i < NUM_STARS; i++) { + // Only draw stars within the game area + if (stars[i][0] >= GAME_X_OFFSET && stars[i][0] < GAME_X_OFFSET + GAME_WIDTH && + stars[i][1] >= GAME_Y_OFFSET && stars[i][1] < GAME_Y_OFFSET + GAME_HEIGHT) { + display.drawPixel(stars[i][0], stars[i][1], WHITE); + } + } +} + +// Improved collision detection using distance formula +bool checkCollision(float x1, float y1, float x2, float y2, float radius) { + return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)) < radius; +} + +// Check if ship is aimed at a saucer +bool isAimedAtSaucer(float ship_x, float ship_y, float ship_rotation, + float saucer_x, float saucer_y, float tolerance) { + // Calculate angle to saucer + float angle_to_saucer = atan2(saucer_y - ship_y, saucer_x - ship_x); + + // Calculate the difference between angles + float angle_diff = getAngleDifference(ship_rotation - PI/2, angle_to_saucer); + + // Check if angle difference is within tolerance + return abs(angle_diff) < tolerance; +} + +// Draw player ship as a dot pattern (PDP-1 style) +void drawShip(float x, float y, float rotation) { + int orig_x = (int)x; + int orig_y = (int)y; + + // Define the rocket shape points in its local coordinate system (PDP-1 style) + const int NUM_SHIP_POINTS = 15; + const int8_t points[][2] = { + {0, -6}, // Top point + {-2, -4}, {2, -4}, // Upper row + {-3, -2}, {3, -2}, // Mid-upper row + {-3, 0}, {3, 0}, // Middle row + {-2, 1}, {2, 1}, // Mid-lower row + {-4, 2}, {0, 2}, {4, 2}, // Lower body row + {-3, 4}, {3, 4} // Bottom fins + }; + + // Draw each point after rotation (PDP-1 style) + for (int i = 0; i < NUM_SHIP_POINTS; i++) { + // Rotate point + float rx = points[i][0] * cos(rotation) - points[i][1] * sin(rotation); + float ry = points[i][0] * sin(rotation) + points[i][1] * cos(rotation); + + // Calculate pixel position + int px = orig_x + (int)rx; + int py = orig_y + (int)ry; + + // Only draw if within game area + if (px >= GAME_X_OFFSET && px < GAME_X_OFFSET + GAME_WIDTH && + py >= GAME_Y_OFFSET && py < GAME_Y_OFFSET + GAME_HEIGHT) { + display.drawPixel(px, py, WHITE); + } + } + + // Add exhaust flame if thrusting - pointing from bottom of rocket + if (ship_thrusting) { + // Calculate bottom center position of the rocket + float bottom_x = 0 * cos(rotation) - 4 * sin(rotation); + float bottom_y = 0 * sin(rotation) + 4 * cos(rotation); + + // Add flame a bit below the bottom center + float flame_x = bottom_x + 2 * sin(rotation); // Perpendicular to rotation + float flame_y = bottom_y - 2 * cos(rotation); // Perpendicular to rotation + + int px = orig_x + (int)flame_x; + int py = orig_y + (int)flame_y; + + // Only draw if within game area + if (px >= GAME_X_OFFSET && px < GAME_X_OFFSET + GAME_WIDTH && + py >= GAME_Y_OFFSET && py < GAME_Y_OFFSET + GAME_HEIGHT) { + display.drawPixel(px, py, WHITE); + } + } +} + +// Helper to clear ship (draws in black) +void clearShip() { + // We'll use a larger area to ensure we cover the ship in any rotation + for (int dy = -8; dy <= 8; dy++) { + for (int dx = -8; dx <= 8; dx++) { + int x = ship_x + dx; + int y = ship_y + dy; + + // Only clear points within the game area + if (x >= GAME_X_OFFSET && x < GAME_X_OFFSET + GAME_WIDTH && + y >= GAME_Y_OFFSET && y < GAME_Y_OFFSET + GAME_HEIGHT) { + // Don't erase stars + bool is_star = false; + for (int i = 0; i < NUM_STARS; i++) { + if (stars[i][0] == x && stars[i][1] == y) { + is_star = true; + break; + } + } + if (!is_star) { + display.drawPixel(x, y, BLACK); + } + } + } + } +} + +// Draw saucer as a dot pattern (PDP-1 style) +void drawSaucer(float x, float y) { + int orig_x = (int)x; + int orig_y = (int)y; + + // Define saucer points (PDP-1 style dot pattern) + const int NUM_SAUCER_POINTS = 18; + const int8_t points[][2] = { + // Top dome + {-1, -2}, {1, -2}, + // Mid-top row + {-4, -1}, {-3, -1}, {3, -1}, {4, -1}, + // Middle row (widest) + {-5, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {5, 0}, + // Mid-bottom row + {-4, 1}, {-3, 1}, {3, 1}, {4, 1}, + // Bottom + {-1, 2}, {1, 2} + }; + + // Draw each point + for (int i = 0; i < NUM_SAUCER_POINTS; i++) { + int px = orig_x + points[i][0]; + int py = orig_y + points[i][1]; + + // Only draw if within game area + if (px >= GAME_X_OFFSET && px < GAME_X_OFFSET + GAME_WIDTH && + py >= GAME_Y_OFFSET && py < GAME_Y_OFFSET + GAME_HEIGHT) { + display.drawPixel(px, py, WHITE); + } + } +} + +// Helper to clear saucer +void clearSaucer(float x, float y) { + // Clear a rectangle around the saucer + for (int dy = -6; dy <= 6; dy++) { + for (int dx = -6; dx <= 6; dx++) { + int px = x + dx; + int py = y + dy; + + // Only clear points within the game area + if (px >= GAME_X_OFFSET && px < GAME_X_OFFSET + GAME_WIDTH && + py >= GAME_Y_OFFSET && py < GAME_Y_OFFSET + GAME_HEIGHT) { + // Don't erase stars + bool is_star = false; + for (int i = 0; i < NUM_STARS; i++) { + if (stars[i][0] == px && stars[i][1] == py) { + is_star = true; + break; + } + } + if (!is_star) { + display.drawPixel(px, py, BLACK); + } + } + } + } +} \ No newline at end of file diff --git a/Factory_Tests/Adafruit_Sparkle_Motion_Stick_FactoryTest/.feather_esp32_v2.test.only b/Factory_Tests/Adafruit_Sparkle_Motion_Stick_FactoryTest/.feather_esp32_v2.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Factory_Tests/Adafruit_Sparkle_Motion_Stick_FactoryTest/Adafruit_Sparkle_Motion_Stick_FactoryTest.ino b/Factory_Tests/Adafruit_Sparkle_Motion_Stick_FactoryTest/Adafruit_Sparkle_Motion_Stick_FactoryTest.ino new file mode 100644 index 000000000..c3075dff6 --- /dev/null +++ b/Factory_Tests/Adafruit_Sparkle_Motion_Stick_FactoryTest/Adafruit_Sparkle_Motion_Stick_FactoryTest.ino @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: 2025 Limor Fried for Adafruit Industries +// +// SPDX-License-Identifier: MIT +#include +#include "WiFi.h" +#include +#include "ESP_I2S.h" +extern Adafruit_TestBed TB; + +// I2S pin definitions +const uint8_t I2S_SCK = 14; // BCLK +const uint8_t I2S_WS = 12; // LRCLK +const uint8_t I2S_DIN = 13; // DATA_IN +I2SClass i2s; + +// the setup routine runs once when you press reset: +void setup() { + Serial.begin(115200); + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + i2s.setPins(I2S_SCK, I2S_WS, -1, I2S_DIN); + if (!i2s.begin(I2S_MODE_STD, 44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO, I2S_STD_SLOT_LEFT)) { + Serial.println("Failed to initialize I2S bus!"); + return; + } + // TestBed will handle the neopixel swirl for us + TB.neopixelPin = PIN_NEOPIXEL; + TB.neopixelNum = 1; + TB.begin(); + + // Set WiFi to station mode and disconnect from an AP if it was previously connected + WiFi.mode(WIFI_STA); + WiFi.disconnect(); +} + +// the loop routine runs over and over again forever: +uint8_t wheelColor=0; +void loop() { + if (wheelColor == 0) { + // Test WiFi Scan! + // WiFi.scanNetworks will return the number of networks found + int n = WiFi.scanNetworks(); + Serial.print("WiFi AP scan done..."); + if (n == 0) { + Serial.println("no networks found"); + } else { + Serial.print(n); + Serial.println(" networks found"); + for (int i = 0; i < n; ++i) { + // Print SSID and RSSI for each network found + Serial.print(i + 1); + Serial.print(": "); + Serial.print(WiFi.SSID(i)); + Serial.print(" ("); + Serial.print(WiFi.RSSI(i)); + Serial.print(")"); + Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*"); + delay(10); + } + } + Serial.println(""); + for (int i=0; i < 5; i++) { + int32_t sample = i2s.read(); + if (sample >= 0){ + Serial.print("Amplitude: "); + Serial.println(sample); + + // Delay to avoid printing too quickly + delay(200); + } + } + } + + TB.setColor(TB.Wheel(wheelColor++)); // swirl NeoPixel + digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); + + delay(5); +} diff --git a/Fruit_Jam/Fruit_Jam_Startups/GameBoy_Startup/code.py b/Fruit_Jam/Fruit_Jam_Startups/GameBoy_Startup/code.py new file mode 100644 index 000000000..dceaffe83 --- /dev/null +++ b/Fruit_Jam/Fruit_Jam_Startups/GameBoy_Startup/code.py @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT +import time +from audiocore import WaveFile +import audiobusio +import board +from displayio import Group, TileGrid, Bitmap, Palette +import supervisor +import adafruit_imageload +import adafruit_tlv320 +from adafruit_fruitjam.peripherals import request_display_config + + +# how long between animation frames +ANIMATE_INTERVAL = 1 / 45 + +background_color = 0xE1F7CE + +i2c = board.I2C() +dac = adafruit_tlv320.TLV320DAC3100(i2c) +dac.configure_clocks(sample_rate=44100, bit_depth=16) +# for headphone jack ouput +dac.headphone_output = True +dac.headphone_volume = -15 # dB +# for speaker JST output +# dac.speaker_output = True +# dac.speaker_volume = -15 # dB + +wave_file = open("gameboy_startup/gameboy_pling.wav", "rb") +wave = WaveFile(wave_file) +audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN) + +# display setup +request_display_config(320, 240) +display = supervisor.runtime.display + +# group to hold all visual elements +main_group = Group() + +# Bitmap for background color +bg_bmp = Bitmap(display.width // 20, display.height // 20, 1) +bg_palette = Palette(1) +bg_palette[0] = background_color +bg_tg = TileGrid(bg_bmp, pixel_shader=bg_palette) + +# group to scale the background bitmap up to display size +bg_group = Group(scale=20) +bg_group.append(bg_tg) +main_group.append(bg_group) + +# Bitmap for logo +logo, palette = adafruit_imageload.load("gameboy_startup/gameboy_logo.bmp") +logo_tg = TileGrid(logo, pixel_shader=palette) +main_group.append(logo_tg) + +# place it in the center horizontally and above the top of the display +logo_tg.x = display.width // 2 - logo_tg.tile_width // 2 +logo_tg.y = -logo_tg.tile_height + +# y pixel location to stop logo at +STOP_Y = display.height * 0.4 - logo_tg.tile_height // 2 + + +display.root_group = main_group +time.sleep(1.5) +last_animate_time = time.monotonic() +played_audio = False +display.auto_refresh = False +while True: + now = time.monotonic() + + # if it's time to animate and the logo isn't to the + # stopping position yet + if last_animate_time + ANIMATE_INTERVAL <= now and logo_tg.y < STOP_Y: + + # update the timestamp + last_animate_time = now + # move the logo down by a pixel + logo_tg.y += 1 + display.refresh() + + # if the logo has reached the stop position + if logo_tg.y >= STOP_Y and not played_audio: + played_audio = True + # play the audio pling + audio.play(wave) + while audio.playing: + pass diff --git a/Fruit_Jam/Fruit_Jam_Startups/GameBoy_Startup/gameboy_startup/gameboy_logo.bmp b/Fruit_Jam/Fruit_Jam_Startups/GameBoy_Startup/gameboy_startup/gameboy_logo.bmp new file mode 100644 index 000000000..79ce257b7 Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/GameBoy_Startup/gameboy_startup/gameboy_logo.bmp differ diff --git a/Fruit_Jam/Fruit_Jam_Startups/GameBoy_Startup/gameboy_startup/gameboy_pling.wav b/Fruit_Jam/Fruit_Jam_Startups/GameBoy_Startup/gameboy_startup/gameboy_pling.wav new file mode 100644 index 000000000..50484cf42 Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/GameBoy_Startup/gameboy_startup/gameboy_pling.wav differ diff --git a/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/code.py b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/code.py new file mode 100644 index 000000000..33be58018 --- /dev/null +++ b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/code.py @@ -0,0 +1,107 @@ +# SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT +import time +from audiocore import WaveFile +import audiobusio +import board +import supervisor +from displayio import Group, TileGrid, OnDiskBitmap +import adafruit_tlv320 +from adafruit_fruitjam.peripherals import request_display_config +from adafruit_progressbar.horizontalprogressbar import ( + HorizontalFillDirection, + HorizontalProgressBar, +) + +# DAC setup +i2c = board.I2C() +dac = adafruit_tlv320.TLV320DAC3100(i2c) +dac.configure_clocks(sample_rate=44100, bit_depth=16) + +# for headphone jack ouput +dac.headphone_output = True +dac.headphone_volume = -15 # dB +# for speaker JST output +# dac.speaker_output = True +# dac.speaker_volume = -15 # dB + +# Chime audio setup +wave_file = open("mac_startup/mac_chime.wav", "rb") +wave = WaveFile(wave_file) +audio = audiobusio.I2SOut(board.I2S_BCLK, board.I2S_WS, board.I2S_DIN) + +# Display setup +request_display_config(640, 480) +display = supervisor.runtime.display +display.auto_refresh = False + +# group to hold visual all elements +main_group = Group() +display.root_group = main_group +display.refresh() + +# background image +bg_bmp = OnDiskBitmap("mac_startup/mac_startup_bg.bmp") +bg_tg = TileGrid(bg_bmp, pixel_shader=bg_bmp.pixel_shader) +main_group.append(bg_tg) + +# Icons for bottom left +icons = [] +for i in range(6): + odb = OnDiskBitmap("mac_startup/mac_startup_icon{0}.bmp".format(i)) + tg = TileGrid(odb, pixel_shader=odb.pixel_shader) + icons.append( + { + "bmp": odb, + "tg": tg, + } + ) + tg.x = 10 + ((33 + 8) * i) + tg.y = display.height - tg.tile_height - 10 + tg.hidden = True + if i < 5: + odb.pixel_shader.make_transparent(0) + main_group.append(tg) + +# progress bar in the welcome box +progress_bar = HorizontalProgressBar( + (147, 138), + (346, 7), + direction=HorizontalFillDirection.LEFT_TO_RIGHT, + min_value=0, + max_value=800, + fill_color=0xC7BEFD, + outline_color=0x000000, + bar_color=0x3F3F3F, + margin_size=0, +) +main_group.append(progress_bar) + +# play the chime sound +audio.play(wave) +while audio.playing: + pass + +# start drawing the visual elements +display.auto_refresh = True +time.sleep(1) +start_time = time.monotonic() + +while True: + elapsed = time.monotonic() - start_time + + # if we haven't reached the end yet + if elapsed * 100 <= 800: + # update the progress bar + progress_bar.value = elapsed * 100 + + else: # reached the end animation + # set progress bar to max value + progress_bar.value = 800 + + # loop over all icons + for index, icon in enumerate(icons): + # if it's time for the current icon to show, and it's still hidden + if (elapsed - 1) > index and icon["tg"].hidden: + # make the current icon visible + icon["tg"].hidden = False diff --git a/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_chime.wav b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_chime.wav new file mode 100644 index 000000000..17e55ed95 Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_chime.wav differ diff --git a/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_bg.bmp b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_bg.bmp new file mode 100644 index 000000000..6ebaf37f9 Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_bg.bmp differ diff --git a/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon0.bmp b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon0.bmp new file mode 100644 index 000000000..837f319b1 Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon0.bmp differ diff --git a/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon1.bmp b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon1.bmp new file mode 100644 index 000000000..73247cc49 Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon1.bmp differ diff --git a/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon2.bmp b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon2.bmp new file mode 100644 index 000000000..ee63e2014 Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon2.bmp differ diff --git a/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon3.bmp b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon3.bmp new file mode 100644 index 000000000..c9c249645 Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon3.bmp differ diff --git a/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon4.bmp b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon4.bmp new file mode 100644 index 000000000..af6a75567 Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon4.bmp differ diff --git a/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon5.bmp b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon5.bmp new file mode 100644 index 000000000..b84e94a8c Binary files /dev/null and b/Fruit_Jam/Fruit_Jam_Startups/Mac_Startup/mac_startup/mac_startup_icon5.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/code.py b/Fruit_Jam/Larsio_Paint_Music/code.py new file mode 100755 index 000000000..4328fa54d --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/code.py @@ -0,0 +1,116 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +Larsio Paint Music +Fruit Jam w mouse, HDMI, audio out +or Metro RP2350 with EYESPI DVI breakout and TLV320DAC3100 breakout on STEMMA_I2C, +pin D7 reset, 9/10/11 = BCLC/WSEL/DIN +""" +# pylint: disable=invalid-name,too-few-public-methods,broad-except,redefined-outer-name + +# Main application file for Larsio Paint Music + +import time +import gc +from sound_manager import SoundManager +from note_manager import NoteManager +from ui_manager import UIManager + +# Configuration +AUDIO_OUTPUT = "i2s" # Options: "pwm" or "i2s" + +class MusicStaffApp: + """Main application class that ties everything together""" + + def __init__(self, audio_output="pwm"): + # Initialize the sound manager with selected audio output + # Calculate tempo parameters + BPM = 120 # Beats per minute + SECONDS_PER_BEAT = 60 / BPM + SECONDS_PER_EIGHTH = SECONDS_PER_BEAT / 2 + + # Initialize components in a specific order + # First, force garbage collection to free memory + gc.collect() + + # Initialize the sound manager + print("Initializing sound manager...") + self.sound_manager = SoundManager( + audio_output=audio_output, + seconds_per_eighth=SECONDS_PER_EIGHTH + ) + + # Give hardware time to stabilize + time.sleep(0.5) + gc.collect() + + # Initialize the note manager + print("Initializing note manager...") + self.note_manager = NoteManager( + start_margin=25, # START_MARGIN + staff_y_start=int(240 * 0.1), # STAFF_Y_START + line_spacing=int((240 - int(240 * 0.1) - int(240 * 0.2)) * 0.95) // 8 # LINE_SPACING + ) + + gc.collect() + + # Initialize the UI manager + print("Initializing UI manager...") + self.ui_manager = UIManager(self.sound_manager, self.note_manager) + + def run(self): + """Set up and run the application""" + # Setup the display and UI + print("Setting up display...") + self.ui_manager.setup_display() + + # Give hardware time to stabilize + time.sleep(0.5) + gc.collect() + + # Try to find the mouse with multiple attempts + MAX_ATTEMPTS = 5 + RETRY_DELAY = 1 # seconds + + mouse_found = False + for attempt in range(MAX_ATTEMPTS): + print(f"Mouse detection attempt {attempt+1}/{MAX_ATTEMPTS}") + if self.ui_manager.find_mouse(): + mouse_found = True + print("Mouse found successfully!") + break + + print(f"Mouse detection attempt {attempt+1} failed, retrying...") + time.sleep(RETRY_DELAY) + + if not mouse_found: + print("WARNING: Mouse not found after multiple attempts.") + print("The application will run, but mouse control may be limited.") + + # Enter the main loop + self.ui_manager.main_loop() + + +# Create and run the application +if __name__ == "__main__": + # Start with garbage collection + gc.collect() + print("Starting Music Staff Application...") + + try: + app = MusicStaffApp(audio_output=AUDIO_OUTPUT) + app.run() + except Exception as e: # pylint: disable=broad-except + print(f"Error with I2S audio: {e}") + + # Force garbage collection + gc.collect() + time.sleep(1) + + # Fallback to PWM + try: + app = MusicStaffApp(audio_output="pwm") + app.run() + except Exception as e2: # pylint: disable=broad-except + print(f"Fatal error: {e2}") diff --git a/Fruit_Jam/Larsio_Paint_Music/control_panel.py b/Fruit_Jam/Larsio_Paint_Music/control_panel.py new file mode 100755 index 000000000..e49ef1e11 --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/control_panel.py @@ -0,0 +1,353 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +# control_panel.py: CircuitPython Music Staff Application component +""" + +# pylint: disable=import-error +from displayio import Group, Bitmap, Palette, TileGrid +from adafruit_display_text.bitmap_label import Label +import terminalio + + +# pylint: disable=invalid-name,no-member,too-many-instance-attributes,too-many-arguments +# pylint: disable=too-many-branches,too-many-statements, trailing-whitespace +class ControlPanel: + """Manages transport controls and channel selectors""" + + def __init__(self, screen_width, screen_height): + self.SCREEN_WIDTH = screen_width + self.SCREEN_HEIGHT = screen_height + + # Button dimensions + self.BUTTON_WIDTH = 64 # Updated for bitmap buttons + self.BUTTON_HEIGHT = 48 # Updated for bitmap buttons + self.BUTTON_SPACING = 10 + + # Channel button dimensions + self.CHANNEL_BUTTON_SIZE = 20 + self.CHANNEL_BUTTON_SPACING = 5 + self.CHANNEL_BUTTON_Y = 5 + + # Transport area + self.TRANSPORT_AREA_Y = (int(screen_height * 0.1) + + int((screen_height - int(screen_height * 0.1) - + int(screen_height * 0.2)) * 0.95) + 10) + + # State + self.is_playing = False + self.loop_enabled = False + + # Channel colors (reduced from 8 to 6) + self.CHANNEL_COLORS = [ + 0x000000, # Channel 1: Black (default) + 0xFF0000, # Channel 2: Red + 0x00FF00, # Channel 3: Green + 0x0000FF, # Channel 4: Blue + 0xFF00FF, # Channel 5: Magenta + 0xFFAA00, # Channel 6: Orange + ] + self.current_channel = 0 + + # UI elements + self.play_button = None + self.stop_button = None + self.loop_button = None + self.clear_button = None + self.play_button_bitmap = None + self.stop_button_bitmap = None + self.loop_button_bitmap = None + self.clear_button_bitmap = None + self.channel_selector = None + + # For bitmap buttons + self.button_sprites = None + + # Center points for fallback play/loop buttons + self.play_center_x = self.BUTTON_WIDTH // 2 + self.play_center_y = self.BUTTON_HEIGHT // 2 + self.play_size = 10 + self.loop_center_x = self.BUTTON_WIDTH // 2 + self.loop_center_y = self.BUTTON_HEIGHT // 2 + self.loop_radius = 6 + + def create_channel_buttons(self): + """Create channel selector buttons at the top of the screen using sprites""" + channel_group = Group() + + # Add a highlight indicator for the selected channel (yellow outline only) + # Create bitmap for channel selector with appropriate dimensions + btn_size = self.CHANNEL_BUTTON_SIZE + channel_select_bitmap = Bitmap(btn_size + 6, btn_size + 6, 2) + channel_select_palette = Palette(2) + channel_select_palette[0] = 0x444444 # Same as background color (dark gray) + channel_select_palette[1] = 0xFFFF00 # Yellow highlight + channel_select_palette.make_transparent(0) # Make background transparent + + # Draw just the outline (no filled background) + bitmap_size = btn_size + 6 + for x in range(bitmap_size): + for y in range(bitmap_size): + # Draw only the border pixels + if (x == 0 or x == bitmap_size - 1 or + y == 0 or y == bitmap_size - 1): + channel_select_bitmap[x, y] = 1 # Yellow outline + else: + channel_select_bitmap[x, y] = 0 # Transparent background + + self.channel_selector = TileGrid( + channel_select_bitmap, + pixel_shader=channel_select_palette, + x=7, + y=self.CHANNEL_BUTTON_Y - 3 + ) + channel_group.append(self.channel_selector) + + return channel_group, self.channel_selector + + def create_transport_controls(self, sprite_manager): + """Create transport controls using bitmap buttons""" + transport_group = Group() + + # Check if button sprites were successfully loaded + if (sprite_manager.play_up is None or sprite_manager.stop_up is None or + sprite_manager.loop_up is None or sprite_manager.clear_up is None): + print("Warning: Button sprites not loaded, using fallback buttons") + return self._create_fallback_transport_controls() + + # Button spacing based on the new size (64x48) + button_spacing = 10 + button_y = self.SCREEN_HEIGHT - 50 # Allow some margin at bottom + + # Create TileGrids for each button using the "up" state initially + self.stop_button = TileGrid( + sprite_manager.stop_up, + pixel_shader=sprite_manager.stop_up_palette, + x=10, + y=button_y + ) + + self.play_button = TileGrid( + sprite_manager.play_up, + pixel_shader=sprite_manager.play_up_palette, + x=10 + 64 + button_spacing, + y=button_y + ) + + self.loop_button = TileGrid( + sprite_manager.loop_up, + pixel_shader=sprite_manager.loop_up_palette, + x=10 + 2 * (64 + button_spacing), + y=button_y + ) + + self.clear_button = TileGrid( + sprite_manager.clear_up, + pixel_shader=sprite_manager.clear_up_palette, + x=10 + 3 * (64 + button_spacing), + y=button_y + ) + + # Store references to the button bitmaps and palettes + self.button_sprites = { + 'play': { + 'up': (sprite_manager.play_up, sprite_manager.play_up_palette), + 'down': (sprite_manager.play_down, sprite_manager.play_down_palette) + }, + 'stop': { + 'up': (sprite_manager.stop_up, sprite_manager.stop_up_palette), + 'down': (sprite_manager.stop_down, sprite_manager.stop_down_palette) + }, + 'loop': { + 'up': (sprite_manager.loop_up, sprite_manager.loop_up_palette), + 'down': (sprite_manager.loop_down, sprite_manager.loop_down_palette) + }, + 'clear': { + 'up': (sprite_manager.clear_up, sprite_manager.clear_up_palette), + 'down': (sprite_manager.clear_down, sprite_manager.clear_down_palette) + } + } + + # Save the button dimensions + self.BUTTON_WIDTH = 64 + self.BUTTON_HEIGHT = 48 + + # Add buttons to the group + transport_group.append(self.stop_button) + transport_group.append(self.play_button) + transport_group.append(self.loop_button) + transport_group.append(self.clear_button) + + return (transport_group, self.play_button, self.stop_button, + self.loop_button, self.clear_button) + + # pylint: disable=too-many-locals + def _create_fallback_transport_controls(self): + """Create fallback transport controls using drawn buttons (original implementation)""" + transport_group = Group() + + # Create button bitmaps + self.play_button_bitmap = Bitmap(self.BUTTON_WIDTH, self.BUTTON_HEIGHT, 3) + self.stop_button_bitmap = Bitmap(self.BUTTON_WIDTH, self.BUTTON_HEIGHT, 3) + self.loop_button_bitmap = Bitmap(self.BUTTON_WIDTH, self.BUTTON_HEIGHT, 3) + self.clear_button_bitmap = Bitmap(self.BUTTON_WIDTH, self.BUTTON_HEIGHT, 3) + + # Button palettes with custom colors + play_button_palette = Palette(3) + play_button_palette[0] = 0x444444 # Dark gray background + play_button_palette[1] = 0x000000 # Black text/border + play_button_palette[2] = 0xFFD700 # Golden yellow for active state + + stop_button_palette = Palette(3) + stop_button_palette[0] = 0x444444 # Dark gray background + stop_button_palette[1] = 0x000000 # Black text/border + stop_button_palette[2] = 0xFF00FF # Magenta for active state + + loop_button_palette = Palette(3) + loop_button_palette[0] = 0x444444 # Dark gray background + loop_button_palette[1] = 0x000000 # Black text/border + loop_button_palette[2] = 0xFFD700 # Golden yellow for active state + + clear_button_palette = Palette(3) + clear_button_palette[0] = 0x444444 # Dark gray background + clear_button_palette[1] = 0x000000 # Black text/border + clear_button_palette[2] = 0xFF0000 # Red for pressed state + + # Create Stop button + for x in range(self.BUTTON_WIDTH): + for y in range(self.BUTTON_HEIGHT): + # Draw border + if (x == 0 or x == self.BUTTON_WIDTH - 1 or + y == 0 or y == self.BUTTON_HEIGHT - 1): + self.stop_button_bitmap[x, y] = 1 + # Fill with magenta (active state) + else: + self.stop_button_bitmap[x, y] = 2 + + # Create Play button + for x in range(self.BUTTON_WIDTH): + for y in range(self.BUTTON_HEIGHT): + # Draw border + if (x == 0 or x == self.BUTTON_WIDTH - 1 or + y == 0 or y == self.BUTTON_HEIGHT - 1): + self.play_button_bitmap[x, y] = 1 + # Fill with gray (inactive state) + else: + self.play_button_bitmap[x, y] = 0 + + # Draw play symbol (triangle) + for y in range( + self.play_center_y - self.play_size//2, + self.play_center_y + self.play_size//2 + ): + width = (y - (self.play_center_y - self.play_size//2)) // 2 + for x in range( + self.play_center_x - self.play_size//4, + self.play_center_x - self.play_size//4 + width + ): + if 0 <= x < self.BUTTON_WIDTH and 0 <= y < self.BUTTON_HEIGHT: + self.play_button_bitmap[x, y] = 1 + + # Create Loop button + for x in range(self.BUTTON_WIDTH): + for y in range(self.BUTTON_HEIGHT): + # Draw border + if (x == 0 or x == self.BUTTON_WIDTH - 1 or + y == 0 or y == self.BUTTON_HEIGHT - 1): + self.loop_button_bitmap[x, y] = 1 + # Fill with gray (inactive state) + else: + self.loop_button_bitmap[x, y] = 0 + + # Draw loop symbol (circle with arrow) + for x in range(self.BUTTON_WIDTH): + for y in range(self.BUTTON_HEIGHT): + dx = x - self.loop_center_x + dy = y - self.loop_center_y + # Draw circle outline + if self.loop_radius - 1 <= (dx*dx + dy*dy)**0.5 <= self.loop_radius + 1: + if 0 <= x < self.BUTTON_WIDTH and 0 <= y < self.BUTTON_HEIGHT: + self.loop_button_bitmap[x, y] = 1 + + # Add arrow to loop symbol + for i in range(4): + x = self.loop_center_x + int(self.loop_radius * 0.7) - i + y = self.loop_center_y - self.loop_radius - 1 + i + if 0 <= x < self.BUTTON_WIDTH and 0 <= y < self.BUTTON_HEIGHT: + self.loop_button_bitmap[x, y] = 1 + + x = self.loop_center_x + int(self.loop_radius * 0.7) - i + y = self.loop_center_y - self.loop_radius - 1 - i + 2 + if 0 <= x < self.BUTTON_WIDTH and 0 <= y < self.BUTTON_HEIGHT: + self.loop_button_bitmap[x, y] = 1 + + # Create Clear button + for x in range(self.BUTTON_WIDTH): + for y in range(self.BUTTON_HEIGHT): + # Draw border + if (x == 0 or x == self.BUTTON_WIDTH - 1 or + y == 0 or y == self.BUTTON_HEIGHT - 1): + self.clear_button_bitmap[x, y] = 1 + # Fill with gray background + else: + self.clear_button_bitmap[x, y] = 0 + + # Create button TileGrids + x_offset = 10 + y_pos = self.SCREEN_HEIGHT - 40 + + self.stop_button = TileGrid( + self.stop_button_bitmap, + pixel_shader=stop_button_palette, + x=x_offset, + y=y_pos + ) + + x_offset += self.BUTTON_WIDTH + self.BUTTON_SPACING + self.play_button = TileGrid( + self.play_button_bitmap, + pixel_shader=play_button_palette, + x=x_offset, + y=y_pos + ) + + x_offset += self.BUTTON_WIDTH + self.BUTTON_SPACING + self.loop_button = TileGrid( + self.loop_button_bitmap, + pixel_shader=loop_button_palette, + x=x_offset, + y=y_pos + ) + + x_offset += self.BUTTON_WIDTH + self.BUTTON_SPACING + self.clear_button = TileGrid( + self.clear_button_bitmap, + pixel_shader=clear_button_palette, + x=x_offset, + y=y_pos + ) + + # Add buttons to group + transport_group.append(self.stop_button) + transport_group.append(self.play_button) + transport_group.append(self.loop_button) + transport_group.append(self.clear_button) + + # Add "CLEAR" text to clear button + text_color = 0x000000 # Black text + label_x = self.clear_button.x + self.BUTTON_WIDTH // 2 + label_y = self.clear_button.y + self.BUTTON_HEIGHT // 2 + + clear_label = Label( + terminalio.FONT, + text="CLEAR", + color=text_color, + scale=1 + ) + clear_label.anchor_point = (0.5, 0.5) # Center the text + clear_label.anchored_position = (label_x, label_y) + transport_group.append(clear_label) + + return (transport_group, self.play_button, self.stop_button, + self.loop_button, self.clear_button) diff --git a/Fruit_Jam/Larsio_Paint_Music/cursor_manager.py b/Fruit_Jam/Larsio_Paint_Music/cursor_manager.py new file mode 100755 index 000000000..995824ed4 --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/cursor_manager.py @@ -0,0 +1,85 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +# cursor_manager.py: CircuitPython Music Staff Application component +""" + +# pylint: disable=import-error +from displayio import Bitmap, Palette, TileGrid + + +# pylint: disable=invalid-name,no-member,too-many-instance-attributes +# pylint: disable=too-many-arguments,too-many-branches,too-many-statements +class CursorManager: + """Manages cursor appearance and position""" + + def __init__(self, bg_color=0x8AAD8A): + self.bg_color = bg_color + + # Cursors + self.crosshair_cursor = None + self.triangle_cursor = None + self.current_cursor = None + + self.create_cursors() + + def create_cursors(self): + """Create custom cursor bitmaps for different areas""" + # Regular crosshair cursor for staff area + crosshair_cursor_bitmap = Bitmap(8, 8, 2) + crosshair_cursor_palette = Palette(2) + crosshair_cursor_palette[0] = self.bg_color # Background color (sage green) + crosshair_cursor_palette[1] = 0x000000 # Cursor color (black) + crosshair_cursor_palette.make_transparent(0) # Make background transparent + + for i in range(8): + crosshair_cursor_bitmap[i, 3] = 1 + crosshair_cursor_bitmap[i, 4] = 1 + crosshair_cursor_bitmap[3, i] = 1 + crosshair_cursor_bitmap[4, i] = 1 + + # Triangle cursor for controls area + triangle_cursor_bitmap = Bitmap(12, 12, 2) + triangle_cursor_palette = Palette(2) + triangle_cursor_palette[0] = 0x000000 # Background color + triangle_cursor_palette[1] = 0x000000 # Cursor color (black) + triangle_cursor_palette.make_transparent(0) # Make background transparent + + # Draw a triangle cursor + for y in range(12): + width = y // 2 + 1 # Triangle gets wider as y increases + for x in range(width): + triangle_cursor_bitmap[x, y] = 1 + + # Create a TileGrid for each cursor type + self.crosshair_cursor = TileGrid( + crosshair_cursor_bitmap, + pixel_shader=crosshair_cursor_palette + ) + self.triangle_cursor = TileGrid( + triangle_cursor_bitmap, + pixel_shader=triangle_cursor_palette + ) + + # Initially use crosshair cursor + self.current_cursor = self.crosshair_cursor + self.triangle_cursor.hidden = True + + return self.crosshair_cursor, self.triangle_cursor + + def set_cursor_position(self, x, y): + """Set the position of the current cursor""" + self.current_cursor.x = x + self.current_cursor.y = y + + def switch_cursor(self, use_triangle=False): + """Switch between crosshair and triangle cursor""" + if use_triangle and self.current_cursor != self.triangle_cursor: + self.crosshair_cursor.hidden = True + self.triangle_cursor.hidden = False + self.current_cursor = self.triangle_cursor + elif not use_triangle and self.current_cursor != self.crosshair_cursor: + self.triangle_cursor.hidden = True + self.crosshair_cursor.hidden = False + self.current_cursor = self.crosshair_cursor diff --git a/Fruit_Jam/Larsio_Paint_Music/display_manager.py b/Fruit_Jam/Larsio_Paint_Music/display_manager.py new file mode 100755 index 000000000..9699c3476 --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/display_manager.py @@ -0,0 +1,64 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +# display_manager.py: CircuitPython Music Staff Application component +""" +# pylint: disable=import-error,invalid-name,no-member,too-many-instance-attributes,too-many-arguments,too-many-branches,too-many-statements + +import displayio +import picodvi +import framebufferio +import board + + + +class DisplayManager: + """Manages the display initialization and basic display operations""" + + + def __init__(self, width=320, height=240): + self.SCREEN_WIDTH = width + self.SCREEN_HEIGHT = height + self.display = None + self.main_group = None + + def initialize_display(self): + """Initialize the DVI display""" + # Release any existing displays + displayio.release_displays() + + # Initialize the DVI framebuffer + fb = picodvi.Framebuffer(self.SCREEN_WIDTH, self.SCREEN_HEIGHT, + clk_dp=board.CKP, clk_dn=board.CKN, + red_dp=board.D0P, red_dn=board.D0N, + green_dp=board.D1P, green_dn=board.D1N, + blue_dp=board.D2P, blue_dn=board.D2N, + color_depth=16) + + # Create the display + self.display = framebufferio.FramebufferDisplay(fb) + + # Create main group + self.main_group = displayio.Group() + + # Set the display's root group + self.display.root_group = self.main_group + + return self.main_group, self.display + + def create_background(self, color=0x888888): + """Create a background with the given color""" + bg_bitmap = displayio.Bitmap(self.SCREEN_WIDTH, self.SCREEN_HEIGHT, 1) + bg_palette = displayio.Palette(1) + bg_palette[0] = color + + # Fill the bitmap with the background color + for x in range(self.SCREEN_WIDTH): + for y in range(self.SCREEN_HEIGHT): + bg_bitmap[x, y] = 0 + + # Create a TileGrid with the background bitmap + bg_grid = displayio.TileGrid(bg_bitmap, pixel_shader=bg_palette, x=0, y=0) + + return bg_grid diff --git a/Fruit_Jam/Larsio_Paint_Music/input_handler.py b/Fruit_Jam/Larsio_Paint_Music/input_handler.py new file mode 100755 index 000000000..1fb7e16d8 --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/input_handler.py @@ -0,0 +1,221 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +# input_handler.py: CircuitPython Music Staff Application component +""" + +import array +import time +import gc + +# pylint: disable=import-error +import usb.core + + +# pylint: disable=invalid-name,no-member,too-many-instance-attributes,too-many-arguments +# pylint: disable=too-many-branches,too-many-statements,broad-except +# pylint: disable=too-many-nested-blocks,too-many-locals,no-self-use +class InputHandler: + """Handles user input through mouse and interactions with UI elements""" + + def __init__(self, screen_width, screen_height, staff_y_start, staff_height): + self.SCREEN_WIDTH = screen_width + self.SCREEN_HEIGHT = screen_height + self.STAFF_Y_START = staff_y_start + self.STAFF_HEIGHT = staff_height + + # Mouse state + self.last_left_button_state = 0 + self.last_right_button_state = 0 + self.left_button_pressed = False + self.right_button_pressed = False + self.mouse = None + self.buf = None + self.in_endpoint = None + + # Mouse position + self.mouse_x = screen_width // 2 + self.mouse_y = screen_height // 2 + + def find_mouse(self): + """Find the mouse device with multiple retry attempts""" + MAX_ATTEMPTS = 5 + RETRY_DELAY = 1 # seconds + + for attempt in range(MAX_ATTEMPTS): + try: + print(f"Mouse detection attempt {attempt+1}/{MAX_ATTEMPTS}") + + # Constants for USB control transfers + DIR_OUT = 0 + # DIR_IN = 0x80 # Unused variable + REQTYPE_CLASS = 1 << 5 + REQREC_INTERFACE = 1 << 0 + HID_REQ_SET_PROTOCOL = 0x0B + + # Find all USB devices + devices_found = False + for device in usb.core.find(find_all=True): + devices_found = True + print(f"Found device: {device.idVendor:04x}:{device.idProduct:04x}") + + try: + # Try to get device info + try: + manufacturer = device.manufacturer + product = device.product + except Exception: # pylint: disable=broad-except + manufacturer = "Unknown" + product = "Unknown" + + # Just use whatever device we find + self.mouse = device + + # Try to detach kernel driver + try: + has_kernel_driver = hasattr(device, 'is_kernel_driver_active') + if has_kernel_driver and device.is_kernel_driver_active(0): + device.detach_kernel_driver(0) + except Exception as e: # pylint: disable=broad-except + print(f"Error detaching kernel driver: {e}") + + # Set configuration + try: + device.set_configuration() + except Exception as e: # pylint: disable=broad-except + print(f"Error setting configuration: {e}") + continue # Try next device + + # Just assume endpoint 0x81 (common for mice) + self.in_endpoint = 0x81 + print(f"Using mouse: {manufacturer}, {product}") + + # Set to report protocol mode + try: + bmRequestType = DIR_OUT | REQTYPE_CLASS | REQREC_INTERFACE + bRequest = HID_REQ_SET_PROTOCOL + wValue = 1 # 1 = report protocol + wIndex = 0 # First interface + + buf = bytearray(1) + device.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, buf) + print("Set to report protocol mode") + except Exception as e: # pylint: disable=broad-except + print(f"Could not set protocol: {e}") + + # Buffer for reading data + self.buf = array.array("B", [0] * 4) + print("Created 4-byte buffer for mouse data") + + # Verify mouse works by reading from it + try: + # Try to read some data with a short timeout + data = device.read(self.in_endpoint, self.buf, timeout=100) + print(f"Mouse test read successful: {data} bytes") + return True + except usb.core.USBTimeoutError: + # Timeout is normal if mouse isn't moving + print("Mouse connected but not sending data (normal)") + return True + except Exception as e: # pylint: disable=broad-except + print(f"Mouse test read failed: {e}") + # Continue to try next device or retry + self.mouse = None + self.in_endpoint = None + continue + + except Exception as e: # pylint: disable=broad-except + print(f"Error initializing device: {e}") + continue + + if not devices_found: + print("No USB devices found") + + # If we get here without returning, no suitable mouse was found + print(f"No working mouse found on attempt {attempt+1}, retrying...") + gc.collect() + time.sleep(RETRY_DELAY) + + except Exception as e: # pylint: disable=broad-except + print(f"Error during mouse detection: {e}") + gc.collect() + time.sleep(RETRY_DELAY) + + print("Failed to find a working mouse after multiple attempts") + return False + + def process_mouse_input(self): + """Process mouse input - simplified version without wheel support""" + try: + # Attempt to read data from the mouse (10ms timeout) + count = self.mouse.read(self.in_endpoint, self.buf, timeout=10) + + if count >= 3: # We need at least buttons, X and Y + # Extract mouse button states + buttons = self.buf[0] + x = self.buf[1] + y = self.buf[2] + + # Convert to signed values if needed + if x > 127: + x = x - 256 + if y > 127: + y = y - 256 + + # Extract button states + current_left_button_state = buttons & 0x01 + current_right_button_state = (buttons & 0x02) >> 1 + + # Detect button presses + if current_left_button_state == 1 and self.last_left_button_state == 0: + self.left_button_pressed = True + else: + self.left_button_pressed = False + + if current_right_button_state == 1 and self.last_right_button_state == 0: + self.right_button_pressed = True + else: + self.right_button_pressed = False + + # Update button states + self.last_left_button_state = current_left_button_state + self.last_right_button_state = current_right_button_state + + # Update position + self.mouse_x += x + self.mouse_y += y + + # Ensure position stays within bounds + self.mouse_x = max(0, min(self.SCREEN_WIDTH - 1, self.mouse_x)) + self.mouse_y = max(0, min(self.SCREEN_HEIGHT - 1, self.mouse_y)) + + return True + + return False + + except usb.core.USBError as e: + # Handle timeouts silently + if e.errno == 110: # Operation timed out + return False + + # Handle disconnections + if e.errno == 19: # No such device + print("Mouse disconnected") + self.mouse = None + self.in_endpoint = None + gc.collect() + + return False + except Exception as e: # pylint: disable=broad-except + print(f"Error reading mouse: {type(e).__name__}") + return False + + def point_in_rect(self, x, y, rect_x, rect_y, rect_width, rect_height): + """Check if a point is inside a rectangle""" + return (rect_x <= x < rect_x + rect_width and + rect_y <= y < rect_y + rect_height) + + def is_over_staff(self, y): + """Check if mouse is over the staff area""" + return self.STAFF_Y_START <= y <= self.STAFF_Y_START + self.STAFF_HEIGHT diff --git a/Fruit_Jam/Larsio_Paint_Music/lpm_icon.bmp b/Fruit_Jam/Larsio_Paint_Music/lpm_icon.bmp new file mode 100644 index 000000000..864c33fa0 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/lpm_icon.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/metadata.json b/Fruit_Jam/Larsio_Paint_Music/metadata.json new file mode 100644 index 000000000..338fedaba --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/metadata.json @@ -0,0 +1,4 @@ +{ + "title": "LarsioPant", + "icon": "lpm_icon.bmp" +} diff --git a/Fruit_Jam/Larsio_Paint_Music/note_manager.py b/Fruit_Jam/Larsio_Paint_Music/note_manager.py new file mode 100755 index 000000000..9a6a9dad3 --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/note_manager.py @@ -0,0 +1,425 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +# note_manager.py: CircuitPython Music Staff Application component +""" + +# pylint: disable=import-error +from displayio import Group, Bitmap, Palette, TileGrid + + +# pylint: disable=invalid-name,no-member,too-many-instance-attributes,too-many-arguments +# pylint: disable=too-many-branches,too-many-statements,protected-access,too-many-locals +# pylint: disable=trailing-whitespace +class NoteManager: + """Manages notes, their positions, and related data""" + + def __init__(self, start_margin, staff_y_start, line_spacing): + self.note_data = [] # List of (x_position, y_position, midi_note, midi_channel) + self.notes_group = Group() + self.ledger_lines_group = Group() + self.note_to_ledger = {} # Mapping from note indices to ledger line indices + + # Key staff parameters + self.START_MARGIN = start_margin + self.STAFF_Y_START = staff_y_start + self.LINE_SPACING = line_spacing + + # Note positions and their MIDI values + self.note_positions = self._create_note_positions() + self.x_positions = [] # Will be populated by the UI manager + + # Create note bitmaps + self.NOTE_WIDTH = (line_spacing // 2) - 2 + self.NOTE_HEIGHT = (line_spacing // 2) - 2 + self.note_bitmap = self._create_note_bitmap() + + # Create ledger line bitmap + self.ledger_line_width = 14 + self.ledger_line_height = 2 + self.ledger_bitmap = Bitmap(self.ledger_line_width, self.ledger_line_height, 2) + for x in range(self.ledger_line_width): + for y in range(self.ledger_line_height): + self.ledger_bitmap[x, y] = 1 + + self.ledger_palette = Palette(2) + self.ledger_palette[0] = 0x8AAD8A # Transparent (sage green background) + self.ledger_palette[1] = 0x000000 # Black for ledger lines + + # MIDI note mapping for each position + self.midi_notes = { + 0: 59, # B3 + 1: 60, # C4 (middle C) + 2: 62, # D4 + 3: 64, # E4 + 4: 65, # F4 + 5: 67, # G4 + 6: 69, # A4 + 7: 71, # B4 + 8: 72, # C5 + 9: 74, # D5 + 10: 76, # E5 + 11: 77, # F5 + 12: 79 # G5 + } + + # Map of positions to note names (for treble clef) + self.note_names = { + 0: "B3", # B below middle C (ledger line) + 1: "C4", # Middle C (ledger line below staff) + 2: "D4", # Space below staff + 3: "E4", # Bottom line + 4: "F4", # First space + 5: "G4", # Second line + 6: "A4", # Second space + 7: "B4", # Middle line + 8: "C5", # Third space + 9: "D5", # Fourth line + 10: "E5", # Fourth space + 11: "F5", # Top line + 12: "G5" # Space above staff + } + + def _create_note_positions(self): + """Create the vertical positions for notes on the staff""" + note_positions = [] + + # Calculate positions from the bottom up + bottom_line_y = self.STAFF_Y_START + 5 * self.LINE_SPACING # Bottom staff line (E) + + # B3 (ledger line below staff) + note_positions.append(bottom_line_y + self.LINE_SPACING + self.LINE_SPACING // 2) + + # Middle C4 (ledger line below staff) + note_positions.append(bottom_line_y + self.LINE_SPACING) + + # D4 (space below staff) + note_positions.append(bottom_line_y + self.LINE_SPACING // 2) + + # E4 (bottom line) + note_positions.append(bottom_line_y) + + # F4 (first space) + note_positions.append(bottom_line_y - self.LINE_SPACING // 2) + + # G4 (second line) + note_positions.append(bottom_line_y - self.LINE_SPACING) + + # A4 (second space) + note_positions.append(bottom_line_y - self.LINE_SPACING - self.LINE_SPACING // 2) + + # B4 (middle line) + note_positions.append(bottom_line_y - 2 * self.LINE_SPACING) + + # C5 (third space) + note_positions.append(bottom_line_y - 2 * self.LINE_SPACING - self.LINE_SPACING // 2) + + # D5 (fourth line) + note_positions.append(bottom_line_y - 3 * self.LINE_SPACING) + + # E5 (fourth space) + note_positions.append(bottom_line_y - 3 * self.LINE_SPACING - self.LINE_SPACING // 2) + + # F5 (top line) + note_positions.append(bottom_line_y - 4 * self.LINE_SPACING) + + # G5 (space above staff) + note_positions.append(bottom_line_y - 4 * self.LINE_SPACING - self.LINE_SPACING // 2) + + return note_positions + + def _create_note_bitmap(self): + """Create a bitmap for a quarter note (circular shape)""" + note_bitmap = Bitmap(self.NOTE_WIDTH, self.NOTE_HEIGHT, 2) + + # Draw a circular shape for the note head + cx = self.NOTE_WIDTH // 2 + cy = self.NOTE_HEIGHT // 2 + radius = self.NOTE_WIDTH // 2 + + for y in range(self.NOTE_HEIGHT): + for x in range(self.NOTE_WIDTH): + # Use the circle equation (x-cx)Β² + (y-cy)Β² ≀ rΒ² to determine if pixel is in circle + if ((x - cx) ** 2 + (y - cy) ** 2) <= (radius ** 2): + note_bitmap[x, y] = 1 + + return note_bitmap + + def find_closest_position(self, y): + """Find the closest valid note position to a given y-coordinate""" + closest_pos = 0 + min_distance = abs(y - self.note_positions[0]) + + for i, pos in enumerate(self.note_positions): + distance = abs(y - pos) + if distance < min_distance: + min_distance = distance + closest_pos = i + + return closest_pos + + def find_closest_x_position(self, x): + """Find the closest valid horizontal position""" + # Only allow positions after the double bar at beginning + if x < self.START_MARGIN: + return self.x_positions[0] # Return first valid position + + closest_x = self.x_positions[0] + min_distance = abs(x - closest_x) + + for pos in self.x_positions: + distance = abs(x - pos) + if distance < min_distance: + min_distance = distance + closest_x = pos + + return closest_x + + def note_exists_at_position(self, x_pos, y_pos, mario_head, mario_palette): + """Check if a note exists at the exact position (for adding new notes)""" + # Only check for exact overlap, not proximity + for note_tg in self.notes_group: + # Check if this is a Mario head note or a regular note + is_mario = (hasattr(note_tg.pixel_shader, "_palette") and + len(note_tg.pixel_shader._palette) > 1 and + note_tg.pixel_shader._palette[0] == mario_palette[0]) + + if is_mario: + note_width = mario_head.width + note_height = mario_head.height + else: + note_width = self.NOTE_WIDTH + note_height = self.NOTE_HEIGHT + + note_x = note_tg.x + note_width // 2 + note_y = note_tg.y + note_height // 2 + + # Only prevent notes from being in the exact same position (with a tiny tolerance) + if abs(note_x - x_pos) < 2 and abs(note_y - y_pos) < 2: + return True + return False + + def find_note_at(self, x, y, mario_head, mario_palette): + """Check if a note already exists at a position and return its index""" + for i, note_tg in enumerate(self.notes_group): + # Check if this is a Mario head note or a regular note + is_mario = (hasattr(note_tg.pixel_shader, "_palette") and + len(note_tg.pixel_shader._palette) > 1 and + note_tg.pixel_shader._palette[0] == mario_palette[0]) + + if is_mario: + note_width = mario_head.width + note_height = mario_head.height + else: + note_width = self.NOTE_WIDTH + note_height = self.NOTE_HEIGHT + + # Check if the note's center is within a reasonable distance of the cursor + note_center_x = note_tg.x + note_width // 2 + note_center_y = note_tg.y + note_height // 2 + + # Use a slightly larger hit box for easier clicking + hit_box_width = max(self.NOTE_WIDTH, note_width) + hit_box_height = max(self.NOTE_HEIGHT, note_height) + + if (abs(x-note_center_x) < hit_box_width) and (abs(y - note_center_y) < hit_box_height): + return i + return None + + def add_note( + self, + x, + y, + current_channel, + note_palettes, + mario_head, + mario_palette, + heart_note, + heart_palette, + sound_manager + ): + """Add a note at the specified position""" + # Enforce the minimum x position (after the double bar at beginning) + if x < self.START_MARGIN: + return (False, "Notes must be after the double bar") + + # Find the closest valid position + position_index = self.find_closest_position(y) + y_position = self.note_positions[position_index] + + # Find the closest valid horizontal position + x_position = self.find_closest_x_position(x) + + # Check if a note already exists at this exact position + if self.note_exists_at_position(x_position, y_position, mario_head, mario_palette): + return (False, "Note already exists here") + + # Get the corresponding MIDI note number + midi_note = self.midi_notes[position_index] + + # Create a TileGrid for the note based on channel + if current_channel == 0: # Channel 1 (index 0) uses Mario head + note_tg = TileGrid(mario_head, pixel_shader=mario_palette) + # Adjust position offset based on the size of mario_head bitmap + note_width = mario_head.width + note_height = mario_head.height + note_tg.x = x_position - note_width // 2 + note_tg.y = y_position - note_height // 2 + elif current_channel == 1: # Channel 2 uses Heart note + note_tg = TileGrid(heart_note, pixel_shader=heart_palette) + # Adjust position offset based on the size of heart_note bitmap + note_width = heart_note.width + note_height = heart_note.height + note_tg.x = x_position - note_width // 2 + note_tg.y = y_position - note_height // 2 + elif current_channel == 2: # Channel 3 uses Drum note + note_tg = TileGrid(mario_head, pixel_shader=mario_palette) + # Adjust position offset based on the size + note_width = mario_head.width + note_height = mario_head.height + note_tg.x = x_position - note_width // 2 + note_tg.y = y_position - note_height // 2 + elif current_channel in (3, 4, 5): # Channels 4-6 use custom sprites + # We'll pass appropriate sprites in ui_manager + note_tg = TileGrid(mario_head, pixel_shader=mario_palette) + note_width = mario_head.width + note_height = mario_head.height + note_tg.x = x_position - note_width // 2 + note_tg.y = y_position - note_height // 2 + else: # Other channels use the colored circle + note_tg = TileGrid(self.note_bitmap, pixel_shader=note_palettes[current_channel]) + note_tg.x = x_position - self.NOTE_WIDTH // 2 + note_tg.y = y_position - self.NOTE_HEIGHT // 2 + + # Play the appropriate sound + sound_manager.play_note(midi_note, current_channel) + + # Add the note to the notes group + note_index = len(self.notes_group) + self.notes_group.append(note_tg) + + # Store the note data for playback with channel information + self.note_data.append((x_position, y_position, midi_note, current_channel)) + + # Add a ledger line if it's the B3 or C4 below staff + if position_index <= 1: # B3 or C4 + ledger_tg = TileGrid(self.ledger_bitmap, pixel_shader=self.ledger_palette) + ledger_tg.x = x_position - self.ledger_line_width // 2 + ledger_tg.y = y_position + ledger_index = len(self.ledger_lines_group) + self.ledger_lines_group.append(ledger_tg) + + # Track association between note and its ledger line + self.note_to_ledger[note_index] = ledger_index + + note_name = self.note_names[position_index] + return (True, f"Added: Ch{current_channel+1} {note_name}") + + def erase_note(self, x, y, mario_head, mario_palette, sound_manager=None): + """Erase a note at the clicked position""" + # Try to find a note at the click position + note_index = self.find_note_at(x, y, mario_head, mario_palette) + + if note_index is not None: + # Get the position of the note + note_tg = self.notes_group[note_index] + + # Check if this is a Mario head note or a regular note + is_mario = (hasattr(note_tg.pixel_shader, "_palette") and + len(note_tg.pixel_shader._palette) > 1 and + note_tg.pixel_shader._palette[0] == mario_palette[0]) + + if is_mario: + note_width = mario_head.width + note_height = mario_head.height + else: + note_width = self.NOTE_WIDTH + note_height = self.NOTE_HEIGHT + + note_x = note_tg.x + note_width // 2 + note_y = note_tg.y + note_height // 2 + + # Find the corresponding note data + found_data_index = None + # found_channel = None # Unused variable + + for i, (x_pos, y_pos, _midi_note, _channel) in enumerate(self.note_data): + # Increased tolerance for position matching + if abs(x_pos - note_x) < 5 and abs(y_pos - note_y) < 5: + found_data_index = i + break + + # If we found the note data and have a sound manager reference + if found_data_index is not None and sound_manager is not None: + # Extract note data + x_pos, y_pos, _midi_note, channel = self.note_data[found_data_index] + + # If this is a sample-based note (channels 0, 1, or 2), stop it + if channel in [0, 1, 2]: + sound_manager.stop_sample_at_position(x_pos, y_pos, channel) + + # Remove the note data + self.note_data.pop(found_data_index) + print(f"Erased note at position ({x_pos}, {y_pos}) ch {channel+1}") + else: + # Still remove the note data if found (for backward compatibility) + if found_data_index is not None: + self.note_data.pop(found_data_index) + + # Check if this note has an associated ledger line + if note_index in self.note_to_ledger: + ledger_index = self.note_to_ledger[note_index] + + # Remove the ledger line + self.ledger_lines_group.pop(ledger_index) + + # Update ledger line mappings after removing a ledger line + new_note_to_ledger = {} + + # Process each mapping + for n_idx, l_idx in self.note_to_ledger.items(): + # Skip the note we're removing + if n_idx != note_index: + # Adjust indices for ledger lines after the removed one + if l_idx > ledger_index: + new_note_to_ledger[n_idx] = l_idx - 1 + else: + new_note_to_ledger[n_idx] = l_idx + + self.note_to_ledger = new_note_to_ledger + + # Remove the note + self.notes_group.pop(note_index) + + # Update mappings for notes with higher indices + new_note_to_ledger = {} + for n_idx, l_idx in self.note_to_ledger.items(): + if n_idx > note_index: + new_note_to_ledger[n_idx - 1] = l_idx + else: + new_note_to_ledger[n_idx] = l_idx + + self.note_to_ledger = new_note_to_ledger + + return (True, "Note erased") + + return (False, "No note found at this position") + + def clear_all_notes(self, sound_manager=None): + """Clear all notes from the staff""" + # Stop all sample playback if we have a sound manager + if sound_manager is not None: + sound_manager.stop_all_notes() + + # Remove all notes + while len(self.notes_group) > 0: + self.notes_group.pop() + + # Remove all ledger lines + while len(self.ledger_lines_group) > 0: + self.ledger_lines_group.pop() + + # Clear note data and ledger line mappings + self.note_data = [] + self.note_to_ledger = {} diff --git a/Fruit_Jam/Larsio_Paint_Music/playback_controller.py b/Fruit_Jam/Larsio_Paint_Music/playback_controller.py new file mode 100755 index 000000000..f6810ae3e --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/playback_controller.py @@ -0,0 +1,143 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +Playback controller for CircuitPython Music Staff Application. +Manages the playback state, button displays, and sound triggering. +""" + +import time + +# pylint: disable=trailing-whitespace, too-many-instance-attributes +class PlaybackController: + """Manages playback state and controls""" + + def __init__(self, sound_manager, note_manager, seconds_per_eighth=0.25): + """Initialize the playback controller with sound and note managers""" + self.sound_manager = sound_manager + self.note_manager = note_manager + self.seconds_per_eighth = seconds_per_eighth + + # Playback state + self.is_playing = False + self.playhead_position = -1 + self.last_playhead_time = 0 + self.loop_enabled = False + + # UI elements (to be set externally) + self.playhead = None + self.play_button = None + self.play_button_bitmap = None + self.stop_button = None + self.stop_button_bitmap = None + + # Button sprites (will be set in set_ui_elements) + self.button_sprites = None + + def set_ui_elements(self, playhead, play_button, stop_button, button_sprites=None): + """Set references to UI elements needed for playback control""" + self.playhead = playhead + self.play_button = play_button + self.stop_button = stop_button + self.button_sprites = button_sprites + + def start_playback(self, start_margin=25): + """Start playback""" + self.is_playing = True + self.playhead_position = -1 # Start at -1 so first note plays immediately + self.last_playhead_time = time.monotonic() + + # Set playhead position to just before the first note + self.playhead.x = start_margin - 5 + + # Update button states using bitmaps + if hasattr(self, 'button_sprites') and self.button_sprites is not None: + # Update play button to "down" state + self.play_button.bitmap = self.button_sprites['play']['down'][0] + self.play_button.pixel_shader = self.button_sprites['play']['down'][1] + + # Update stop button to "up" state + self.stop_button.bitmap = self.button_sprites['stop']['up'][0] + self.stop_button.pixel_shader = self.button_sprites['stop']['up'][1] + else: + # Fallback implementation for drawn buttons + # Note: This section is for backward compatibility but has issues + # Ideally, button_sprites should always be provided + print("Warning: Using fallback button display (not fully supported)") + # The fallback code is intentionally omitted as it has errors + # and requires refactoring of the bitmap handling + + print("Playback started") + + def stop_playback(self): + """Stop playback""" + self.sound_manager.stop_all_notes() + self.is_playing = False + self.playhead.x = -10 # Move off-screen + + # Update button states using bitmaps + if hasattr(self, 'button_sprites') and self.button_sprites is not None: + # Update play button to "up" state + self.play_button.bitmap = self.button_sprites['play']['up'][0] + self.play_button.pixel_shader = self.button_sprites['play']['up'][1] + + # Update stop button to "down" state + self.stop_button.bitmap = self.button_sprites['stop']['down'][0] + self.stop_button.pixel_shader = self.button_sprites['stop']['down'][1] + else: + # Fallback implementation for drawn buttons + # Note: This section is for backward compatibility but has issues + # Ideally, button_sprites should always be provided + print("Warning: Using fallback button display (not fully supported)") + # The fallback code is intentionally omitted as it has errors + # and requires refactoring of the bitmap handling + + print("Playback stopped") + + def set_tempo(self, seconds_per_eighth): + """Update the playback tempo""" + self.seconds_per_eighth = seconds_per_eighth + print(f"Playback tempo updated: {60 / (seconds_per_eighth * 2)} BPM") + + def update_playback(self, x_positions): + """Update playback state and play notes at current position""" + if not self.is_playing: + return + + current_time = time.monotonic() + elapsed = current_time - self.last_playhead_time + + # Move at tempo rate + if elapsed >= self.seconds_per_eighth: + # Stop all current active notes + self.sound_manager.stop_all_notes() + + # Move playhead to next eighth note position + self.playhead_position += 1 + self.last_playhead_time = current_time + + # Check if we've reached the end + if self.playhead_position >= len(x_positions): + if self.loop_enabled: + # Loop back to the beginning + self.playhead_position = 0 + self.playhead.x = x_positions[0] - 1 + else: + # Stop playback if not looping + self.stop_playback() + return + + # Update playhead position + self.playhead.x = x_positions[self.playhead_position] - 1 + + # Find all notes at current playhead position + current_x = x_positions[self.playhead_position] + notes_at_position = [] + + for x_pos, y_pos, midi_note, channel in self.note_manager.note_data: + if abs(x_pos - current_x) < 2: # Note is at current position + notes_at_position.append((x_pos, y_pos, midi_note, channel)) + + # Play all notes at the current position + if notes_at_position: + self.sound_manager.play_notes_at_position(notes_at_position) diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/chat_01.wav b/Fruit_Jam/Larsio_Paint_Music/samples/chat_01.wav new file mode 100755 index 000000000..f4606c333 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/chat_01.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/crash_01.wav b/Fruit_Jam/Larsio_Paint_Music/samples/crash_01.wav new file mode 100755 index 000000000..3d0c64acb Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/crash_01.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/kick_01.wav b/Fruit_Jam/Larsio_Paint_Music/samples/kick_01.wav new file mode 100755 index 000000000..f404cf804 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/kick_01.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_A4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_A4.wav new file mode 100755 index 000000000..0e8f56621 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_A4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_B3.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_B3.wav new file mode 100755 index 000000000..92331cc65 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_B3.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_B4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_B4.wav new file mode 100755 index 000000000..b71c7f80e Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_B4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_C4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_C4.wav new file mode 100755 index 000000000..f0ca34ec1 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_C4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_C5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_C5.wav new file mode 100755 index 000000000..da435ddc6 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_C5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_D4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_D4.wav new file mode 100755 index 000000000..e9120da70 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_D4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_D5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_D5.wav new file mode 100755 index 000000000..872406794 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_D5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_E4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_E4.wav new file mode 100755 index 000000000..151a141ed Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_E4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_E5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_E5.wav new file mode 100755 index 000000000..8d8c45c19 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_E5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_F4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_F4.wav new file mode 100755 index 000000000..419c8e4bb Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_F4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_F5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_F5.wav new file mode 100755 index 000000000..7fd14ff10 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_F5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_G4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_G4.wav new file mode 100755 index 000000000..eab079aa1 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_G4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/larso_G5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/larso_G5.wav new file mode 100755 index 000000000..22cdae793 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/larso_G5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_A4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_A4.wav new file mode 100755 index 000000000..4173e55c4 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_A4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_B3.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_B3.wav new file mode 100755 index 000000000..da6d8f84d Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_B3.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_B4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_B4.wav new file mode 100755 index 000000000..7a2ea264a Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_B4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_C4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_C4.wav new file mode 100755 index 000000000..b870ac3c4 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_C4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_C5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_C5.wav new file mode 100755 index 000000000..50febe988 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_C5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_D4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_D4.wav new file mode 100755 index 000000000..843ada638 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_D4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_D5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_D5.wav new file mode 100755 index 000000000..f43f9a1e3 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_D5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_E4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_E4.wav new file mode 100755 index 000000000..609095e70 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_E4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_E5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_E5.wav new file mode 100755 index 000000000..561b45931 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_E5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_F4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_F4.wav new file mode 100755 index 000000000..081be3cb2 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_F4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_F5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_F5.wav new file mode 100755 index 000000000..96b4b6640 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_F5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_G4.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_G4.wav new file mode 100755 index 000000000..31f19a0e1 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_G4.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_G5.wav b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_G5.wav new file mode 100755 index 000000000..7a0b5fd8c Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/musicnote16_G5.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/ohat_01.wav b/Fruit_Jam/Larsio_Paint_Music/samples/ohat_01.wav new file mode 100755 index 000000000..3c41e6a3a Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/ohat_01.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/samples/snare_01.wav b/Fruit_Jam/Larsio_Paint_Music/samples/snare_01.wav new file mode 100755 index 000000000..004be30a8 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/samples/snare_01.wav differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sound_manager.py b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py new file mode 100755 index 000000000..5746f873b --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/sound_manager.py @@ -0,0 +1,613 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +# sound_manager.py: CircuitPython Music Staff Application component +""" +# pylint: disable=import-error, trailing-whitespace +# +import math +import time +import array +import gc +import os +import digitalio +import busio + + +import adafruit_midi +import audiocore +import audiopwmio +import audiobusio +import audiomixer +import synthio +import board +import adafruit_tlv320 +from adafruit_midi.note_on import NoteOn +from adafruit_midi.note_off import NoteOff +import usb_midi + + +# pylint: disable=invalid-name,no-member,too-many-instance-attributes,too-many-arguments +# pylint: disable=too-many-branches,too-many-statements,too-many-locals,broad-except +# pylint: disable=cell-var-from-loop,undefined-loop-variable +class SoundManager: + """Handles playback of both MIDI notes and WAV samples, and synthio for channels 3-5""" + + def __init__(self, audio_output="pwm", seconds_per_eighth=0.25): + """ + Initialize the sound manager + + Parameters: + audio_output (str): The type of audio output to use - "pwm" or "i2s" + seconds_per_eighth (float): Duration of an eighth note in seconds + """ + # Initialize USB MIDI + self.midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0) + self.active_notes = {} # {note_number: channel} + + # Store timing information + self.seconds_per_eighth = seconds_per_eighth + + # Initialize audio output based on selected type + self.audio_output_type = audio_output + self.tlv = None + + # Initialize these variables to avoid use-before-assignment issues + i2c = None + bclck_pin = None + wsel_pin = None + din_pin = None + + if self.audio_output_type == "pwm": + # Setup PWM audio output on D10 + self.audio = audiopwmio.PWMAudioOut(board.D10) + else: # i2s + try: + # Import libraries needed for I2S + #check for Metro RP2350 vs. Fruit Jam + board_type = os.uname().machine + + if 'Metro RP2350' in board_type: + print("Metro setup") + reset_pin = digitalio.DigitalInOut(board.D7) + reset_pin.direction = digitalio.Direction.OUTPUT + reset_pin.value = False # Set low to reset + time.sleep(0.1) # Pause 100ms + reset_pin.value = True # Set high to release from reset + + i2c = board.STEMMA_I2C() # initialize I2C + + bclck_pin = board.D9 + wsel_pin = board.D10 + din_pin = board.D11 + + elif 'Fruit Jam' in board_type: + print("Fruit Jam setup") + reset_pin = digitalio.DigitalInOut(board.PERIPH_RESET) + reset_pin.direction = digitalio.Direction.OUTPUT + reset_pin.value = False + time.sleep(0.1) + reset_pin.value = True + + i2c = busio.I2C(board.SCL, board.SDA) + + bclck_pin = board.I2S_BCLK + wsel_pin = board.I2S_WS + din_pin = board.I2S_DIN + + # Initialize TLV320 + self.tlv = adafruit_tlv320.TLV320DAC3100(i2c) + self.tlv.configure_clocks(sample_rate=11025, bit_depth=16) + self.tlv.headphone_output = True + self.tlv.headphone_volume = -15 # dB + + # Setup I2S audio output - important to do this AFTER configuring the DAC + self.audio = audiobusio.I2SOut( + bit_clock=bclck_pin, + word_select=wsel_pin, + data=din_pin + ) + + print("TLV320 I2S DAC initialized successfully") + except Exception as e: + print(f"Error initializing TLV320 DAC: {e}") + print("Falling back to PWM audio output") + # Fallback to PWM if I2S initialization fails + self.audio = audiopwmio.PWMAudioOut(board.D10) + + # Create an audio mixer with multiple voices + self.mixer = audiomixer.Mixer( + voice_count=6, + sample_rate=11025, + channel_count=1, + bits_per_sample=16, + samples_signed=True + ) + self.audio.play(self.mixer) + + # Track which voices are being used for samples + # First 3 for regular samples, next 3 for playback-only + self.active_voices = [False, False, False, False, False, False] + + # Track which note position corresponds to which voice + # This will help us stop samples when notes are erased + self.position_to_voice = {} # {(x_pos, y_pos): voice_index} + + # Track which voice is used for which channel during playback + self.playback_voice_mapping = {} # {(x_pos, y_pos, channel): voice_index} + + # Load multiple WAV samples at different pitches + try: + # Channel 1 samples + self.samples = { + 59: audiocore.WaveFile("/samples/larso_B3.wav"), # B3 + 60: audiocore.WaveFile("/samples/larso_C4.wav"), # C4 + 62: audiocore.WaveFile("/samples/larso_D4.wav"), # D4 + 64: audiocore.WaveFile("/samples/larso_E4.wav"), # E4 + 65: audiocore.WaveFile("/samples/larso_F4.wav"), # F4 + 67: audiocore.WaveFile("/samples/larso_G4.wav"), # G4 + 69: audiocore.WaveFile("/samples/larso_A4.wav"), # A4 + 71: audiocore.WaveFile("/samples/larso_B4.wav"), # B4 + 72: audiocore.WaveFile("/samples/larso_C5.wav"), # C5 + 74: audiocore.WaveFile("/samples/larso_D5.wav"), # D5 + 76: audiocore.WaveFile("/samples/larso_E5.wav"), # E5 + 77: audiocore.WaveFile("/samples/larso_F5.wav"), # F5 + 79: audiocore.WaveFile("/samples/larso_G5.wav"), # G5 + } + print("Loaded channel 1 WAV samples") + + # Load samples for channel 2 + self.heart_samples = { + 59: audiocore.WaveFile("/samples/musicnote16_B3.wav"), # B3 + 60: audiocore.WaveFile("/samples/musicnote16_C4.wav"), # C4 + 62: audiocore.WaveFile("/samples/musicnote16_D4.wav"), # D4 + 64: audiocore.WaveFile("/samples/musicnote16_E4.wav"), # E4 + 65: audiocore.WaveFile("/samples/musicnote16_F4.wav"), # F4 + 67: audiocore.WaveFile("/samples/musicnote16_G4.wav"), # G4 + 69: audiocore.WaveFile("/samples/musicnote16_A4.wav"), # A4 + 71: audiocore.WaveFile("/samples/musicnote16_B4.wav"), # B4 + 72: audiocore.WaveFile("/samples/musicnote16_C5.wav"), # C5 + 74: audiocore.WaveFile("/samples/musicnote16_D5.wav"), # D5 + 76: audiocore.WaveFile("/samples/musicnote16_E5.wav"), # E5 + 77: audiocore.WaveFile("/samples/musicnote16_F5.wav"), # F5 + 79: audiocore.WaveFile("/samples/musicnote16_G5.wav"), # G5 + } + print("Loaded channel 2 WAV samples") + + # Load samples for channel 3 (drum samples) + self.drum_samples = {} + try: + self.drum_samples = { + 59: audiocore.WaveFile("/samples/kick_01.wav"), + 60: audiocore.WaveFile("/samples/kick_01.wav"), + 62: audiocore.WaveFile("/samples/kick_01.wav"), + 64: audiocore.WaveFile("/samples/snare_01.wav"), + 65: audiocore.WaveFile("/samples/snare_01.wav"), + 67: audiocore.WaveFile("/samples/snare_01.wav"), + 69: audiocore.WaveFile("/samples/chat_01.wav"), + 71: audiocore.WaveFile("/samples/chat_01.wav"), + 72: audiocore.WaveFile("/samples/chat_01.wav"), + 74: audiocore.WaveFile("/samples/ohat_01.wav"), + 76: audiocore.WaveFile("/samples/ohat_01.wav"), + 77: audiocore.WaveFile("/samples/crash_01.wav"), + 79: audiocore.WaveFile("/samples/crash_01.wav"), + } + print("Loaded channel 3 WAV samples (drums)") + except Exception as e: + print(f"Error loading drum samples: {e}") + # Fallback - use the same samples as channel 1 + self.drum_samples = self.samples + print("Using fallback samples for channel 3") + + except Exception as e: + print(f"Error loading WAV samples: {e}") + # Fallback to basic samples if there's an error + self.samples = { + 65: audiocore.WaveFile("/samples/musicnote01.wav"), # Default sample + } + self.heart_samples = self.samples # Use same samples as fallback + self.drum_samples = self.samples # Use same samples as fallback + + # Initialize synthio for channels 4-6 + self.synth = synthio.Synthesizer(sample_rate=11025) + # Use the last voice for synthio + self.mixer.voice[5].play(self.synth) + + # Set lower volume for synthio channel + self.mixer.voice[5].level = 0.3 + + # Create waveforms for different synthio channels + SAMPLE_SIZE = 512 + SAMPLE_VOLUME = 30000 # Slightly lower to avoid overflow + half_period = SAMPLE_SIZE // 2 + + # Sine wave for channel 4 + self.wave_sine = array.array("h", [0] * SAMPLE_SIZE) + for i in range(SAMPLE_SIZE): + # Use max() and min() to ensure we stay within bounds + value = int(math.sin(math.pi * 2 * (i/2) / SAMPLE_SIZE) * SAMPLE_VOLUME) + self.wave_sine[i] = max(-32768, min(32767, value)) + + # Triangle wave for channel 5 + self.wave_tri = array.array("h", [0] * SAMPLE_SIZE) + for i in range(SAMPLE_SIZE): + if i < half_period: + value = int(((i / (half_period)) * 2 - 1) * SAMPLE_VOLUME) + else: + value = int(((2 - (i / (half_period)) * 2)) * SAMPLE_VOLUME) + self.wave_tri[i] = max(-32768, min(32767, value)) + + # Sawtooth wave for channel 6 + self.wave_saw = array.array("h", [0] * SAMPLE_SIZE) + for i in range(SAMPLE_SIZE): + value = int(((i / SAMPLE_SIZE) * 2 - 1) * SAMPLE_VOLUME) + self.wave_saw[i] = max(-32768, min(32767, value)) + + # Map channels to waveforms + self.channel_waveforms = { + 3: self.wave_sine, # Channel 4: Sine wave (soft, pure tone) + 4: self.wave_tri, # Channel 5: Triangle wave (mellow, soft) + 5: self.wave_saw, # Channel 6: Sawtooth wave (brassy, sharp) + } + + # Set different amplitudes for each waveform to balance volumes + self.channel_amplitudes = { + 3: 1.0, # Sine wave - normal volume + 4: 0.8, # Triangle wave - slightly quieter + 5: 0.3, # Sawtooth wave - much quieter (harmonically rich) + } + + # Track active synth notes by channel and note + self.active_synth_notes = { + 3: [], # Channel 4 + 4: [], # Channel 5 + 5: [], # Channel 6 + } + + # Variables for timed release of preview notes + self.note_release_time = 0 + self.note_to_release = None + self.note_to_release_channel = None + self.preview_mode = False + + def play_note(self, midi_note, channel): + """Play a note using either MIDI, WAV, or synthio based on channel""" + if channel == 0: # Channel 1 uses WAV samples + self.play_multi_sample(midi_note, channel) + elif channel == 1: # Channel 2 uses Heart note WAV samples + self.play_multi_sample(midi_note, channel) + elif channel == 2: # Channel 3 uses Drum WAV samples + self.play_multi_sample(midi_note, channel) + elif channel in [3, 4, 5]: # Channels 4-6 use synthio with different waveforms + self.preview_mode = True + self.play_synth_note(midi_note, channel) + # Schedule note release + self.note_release_time = time.monotonic() + self.seconds_per_eighth + self.note_to_release_channel = channel + else: + # Send note on the correct MIDI channel (channels are 0-based in adafruit_midi) + self.midi.send(NoteOn(midi_note, 100), channel=channel) + # Store note with its channel for proper Note Off later + self.active_notes[midi_note] = channel + # print(f"Playing note: {midi_note} on channel {channel + 1}") + + def play_notes_at_position(self, notes_data): + """Play all notes at a specific position simultaneously""" + # Stop all sample voices first + for i in range(5): # Use first 5 voices for WAV samples (0-4) + self.mixer.voice[i].stop() + self.active_voices[i] = False + + # Clear the position to voice mapping + self.position_to_voice = {} + self.playback_voice_mapping = {} + + # Group notes by channel type + sample_notes = { + 0: [], # Channel 1 (Lars WAV samples) + 1: [], # Channel 2 (Heart WAV samples) + 2: [] # Channel 3 (Drum WAV samples) + } + + # Synthio channels (4-6) + synth_notes = { + 3: [], # Channel 4 (Sine wave) + 4: [], # Channel 5 (Triangle wave) + 5: [], # Channel 6 (Sawtooth wave) + } + + midi_notes = {} # Other channels (MIDI) + + for x_pos, y_pos, note_val, channel in notes_data: + if channel in [0, 1, 2]: # Sample-based channels + sample_notes[channel].append((x_pos, y_pos, note_val)) + elif channel in [3, 4, 5]: # Synthio channels + synth_notes[channel].append(note_val) + else: # Other channels (MIDI) + midi_notes[note_val] = channel + + # Voice allocation - we have 5 voices to distribute among sample notes + remaining_voices = 5 + voice_index = 0 + + # Play sample notes for channels 1-3 + for channel, notes in sample_notes.items(): + for x_pos, y_pos, midi_note in notes: + if remaining_voices <= 0: + print(f"Warning: No more voices available for channel {channel+1}") + break + + # Get the appropriate sample set + sample_set = None + if channel == 0: + sample_set = self.samples + elif channel == 1: + sample_set = self.heart_samples + elif channel == 2: + sample_set = self.drum_samples + + # Find the closest sample + closest_note = min(sample_set.keys(), key=lambda x: abs(x - midi_note)) + sample = sample_set[closest_note] + + # Play the sample + self.mixer.voice[voice_index].play(sample, loop=False) + self.active_voices[voice_index] = True + + # Store the position to voice mapping + position_key = (x_pos, y_pos) + self.position_to_voice[position_key] = voice_index + self.playback_voice_mapping[(x_pos, y_pos, channel)] = voice_index + + # Adjust volume + total_notes = sum(len(notes) for notes in sample_notes.values()) + volume_factor = 0.9 if total_notes <= 3 else 0.7 if total_notes <= 6 else 0.5 + self.mixer.voice[voice_index].level = 0.7 * volume_factor + + voice_index += 1 + remaining_voices -= 1 + + # Log what we're playing + # Channel names commented out as it was unused + # channel_names = ["Lars", "Heart", "Drum"] + # print(f"Playing {channel_names[channel]} sample {closest_note} for note {midi_note}") + + # Play synth notes for each channel (4-6) + self.preview_mode = False + for channel, notes in synth_notes.items(): + for note in notes: + self.play_synth_note(note, channel) + + # Play MIDI notes + for midi_note, channel in midi_notes.items(): + self.midi.send(NoteOn(midi_note, 100), channel=channel) + self.active_notes[midi_note] = channel + + def play_multi_sample(self, midi_note, channel=0): + """Play the most appropriate sample for the given MIDI note""" + try: + # Find an available voice (use first 3 voices for interactive play) + voice_index = -1 + for i in range(3): # Only use the first 3 voices for interactive playback + if not self.active_voices[i]: + voice_index = i + break + + # If all voices are active, use the first one + if voice_index == -1: + voice_index = 0 + + # Stop any currently playing sample in this voice + self.mixer.voice[voice_index].stop() + + # Select the appropriate sample set based on channel + if channel == 1: # Heart samples + sample_set = self.heart_samples + elif channel == 2: # Drum samples + sample_set = self.drum_samples + else: # Default to channel 1 samples + sample_set = self.samples + + # Find the closest sample + closest_note = min(sample_set.keys(), key=lambda x: abs(x - midi_note)) + + # Get the sample + sample = sample_set[closest_note] + + # Play the sample + self.mixer.voice[voice_index].play(sample, loop=False) + self.active_voices[voice_index] = True + + # Adjust volume based on which sample we're using + if closest_note == 65: # F4 + self.mixer.voice[voice_index].level = 0.8 + elif closest_note == 69: # A4 + self.mixer.voice[voice_index].level = 0.7 + elif closest_note == 72: # C5 + self.mixer.voice[voice_index].level = 0.6 + else: + self.mixer.voice[voice_index].level = 0.7 + + except Exception as e: + print(f"Error playing multi-sample: {e}") + # Try to play any available sample as a fallback + if len(self.samples) > 0: + first_sample = next(iter(self.samples.values())) + self.mixer.voice[0].play(first_sample, loop=False) + + def play_synth_note(self, midi_note, channel): + """Play a note using synthio with different waveforms per channel""" + try: + # Convert MIDI note to frequency + frequency = 440 * math.pow(2, (midi_note - 69) / 12) + + # Get the appropriate waveform for this channel + waveform = self.channel_waveforms.get(channel, self.wave_sine) + + # Get the appropriate amplitude for this channel + amplitude = self.channel_amplitudes.get(channel, 1.0) + + # Create synthio note with the specific waveform and amplitude + note = synthio.Note( + frequency, + waveform=waveform, + amplitude=amplitude + ) + + # Add to synth + self.synth.press(note) + + # If we have an existing preview note to release, release it first + if self.preview_mode and self.note_to_release and self.note_to_release_channel==channel: + try: + self.synth.release(self.note_to_release) + except Exception as e: + print(f"Error releasing previous note: {e}") + + # Store the new note for scheduled release if in preview mode + if self.preview_mode: + self.note_to_release = note + self.note_to_release_channel = channel + else: + self.active_synth_notes[channel].append(note) + + except Exception as e: + print(f"Error playing synthio note: {e}") + # If there's an error with custom waveforms, fall back to default note + try: + frequency = 440 * math.pow(2, (midi_note - 69) / 12) + note = synthio.Note(frequency) + self.synth.press(note) + + # Store for later release + if self.preview_mode: + self.note_to_release = note + self.note_to_release_channel = channel + else: + self.active_synth_notes[channel].append(note) + + except Exception as e2: + print(f"Fallback note error: {e2}") + + def stop_sample_at_position(self, x_pos, y_pos, channel): + """Stop a sample that's playing at the given position for a specific channel""" + position_key = (x_pos, y_pos, channel) + if position_key in self.playback_voice_mapping: + voice_index = self.playback_voice_mapping[position_key] + + # Stop the sample + self.mixer.voice[voice_index].stop() + self.active_voices[voice_index] = False + + # Remove from mappings + del self.playback_voice_mapping[position_key] + return True + + # Also check the simple position mapping + simple_key = (x_pos, y_pos) + if simple_key in self.position_to_voice: + voice_index = self.position_to_voice[simple_key] + + # Stop the sample + self.mixer.voice[voice_index].stop() + self.active_voices[voice_index] = False + + # Remove from mapping + del self.position_to_voice[simple_key] + return True + + return False + + def update(self): + """Update function to handle timed note releases""" + # Check if we need to release a preview note + if self.note_to_release and time.monotonic() >= self.note_release_time: + try: + self.synth.release(self.note_to_release) + self.note_to_release = None + self.note_to_release_channel = None + except Exception as e: + print(f"Error releasing preview note: {e}") + self.note_to_release = None + self.note_to_release_channel = None + + def stop_all_notes(self): + """Stop all currently playing notes""" + # Stop all MIDI notes + for note_number, channel in self.active_notes.items(): + self.midi.send(NoteOff(note_number, 0), channel=channel) + self.active_notes = {} + + # Stop all WAV samples + for i in range(5): # Use first 5 voices for WAV samples + self.mixer.voice[i].stop() + self.active_voices[i] = False + + # Clear position mappings + self.position_to_voice = {} + self.playback_voice_mapping = {} + + # Stop all synth notes + try: + # Release notes from all channels + for channel, notes in self.active_synth_notes.items(): + for note in notes: + self.synth.release(note) + self.active_synth_notes[channel] = [] + + # Also release preview note if there is one + if self.note_to_release: + self.synth.release(self.note_to_release) + self.note_to_release = None + self.note_to_release_channel = None + + except Exception as e: + print(f"Error releasing synth notes: {e}") + # Reinitialize the synth as a fallback + try: + self.synth.deinit() + self.synth = synthio.Synthesizer(sample_rate=11025) + self.mixer.voice[5].play(self.synth) + + # Reset all active notes + self.active_synth_notes = { + 3: [], # Channel 4 + 4: [], # Channel 5 + 5: [], # Channel 6 + } + except Exception as e2: + print(f"Error reinitializing synth: {e2}") + + def deinit(self): + """Clean up resources when shutting down""" + # Stop all sounds + self.stop_all_notes() + + # Clean up audio resources + try: + self.audio.deinit() + except Exception: + pass + + # Power down the TLV320 if applicable + if self.tlv: + try: + # For TLV320DAC3100, headphone_output = False will power down the output + self.tlv.headphone_output = False + except Exception: + pass + + # Clean up synth + try: + self.synth.deinit() + except Exception: + pass + + # Force garbage collection + gc.collect() + + def set_tempo(self, seconds_per_eighth): + """Update the playback tempo""" + self.seconds_per_eighth = seconds_per_eighth + print(f"Playback tempo updated: {60 / (seconds_per_eighth * 2)} BPM") diff --git a/Fruit_Jam/Larsio_Paint_Music/sprite_manager.py b/Fruit_Jam/Larsio_Paint_Music/sprite_manager.py new file mode 100755 index 000000000..1e6a513c9 --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/sprite_manager.py @@ -0,0 +1,219 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +Sprite manager for CircuitPython Music Staff Application. +Handles loading and managing sprite images and palettes. +""" + +# pylint: disable=import-error, trailing-whitespace +import adafruit_imageload +from displayio import Palette, TileGrid + + +# pylint: disable=too-many-instance-attributes,invalid-name,broad-except +class SpriteManager: + """Manages sprites and palettes for note display""" + + def __init__(self, bg_color=0x8AAD8A): + """Initialize the sprite manager""" + self.bg_color = bg_color + + # Initialize palettes as empty lists first + self.note_palettes = [] + self.preview_palettes = [] + + # Sprites + self.mario_head = None + self.mario_palette = None + self.heart_note = None + self.heart_palette = None + self.drum_note = None + self.drum_palette = None + # Add new sprite variables + self.meatball_note = None + self.meatball_palette = None + self.star_note = None + self.star_palette = None + self.bot_note = None + self.bot_palette = None + + # Channel colors (still need these for palette management) + self.channel_colors = [ + 0x000000, # Channel 1: Black (default) + 0xFF0000, # Channel 2: Red + 0x00FF00, # Channel 3: Green + 0x0000FF, # Channel 4: Blue + 0xFF00FF, # Channel 5: Magenta + 0xFFAA00, # Channel 6: Orange + ] + + # Add button sprites + self.play_up = None + self.play_up_palette = None + self.play_down = None + self.play_down_palette = None + self.stop_up = None + self.stop_up_palette = None + self.stop_down = None + self.stop_down_palette = None + self.loop_up = None + self.loop_up_palette = None + self.loop_down = None + self.loop_down_palette = None + self.clear_up = None + self.clear_up_palette = None + self.clear_down = None + self.clear_down_palette = None + + # Load sprites + self.load_sprites() + + # Load button sprites + self.load_button_sprites() + + # Create palettes + self.create_palettes() + + def load_sprites(self): + """Load sprite images""" + try: + # Load the Lars note bitmap for channel 1 notes + self.mario_head, self.mario_palette = adafruit_imageload.load( + "/sprites/lars_note.bmp" + ) + # Make the background color transparent (not just the same color) + self.mario_palette.make_transparent(0) + + # Load the Heart note bitmap for channel 2 notes + self.heart_note, self.heart_palette = adafruit_imageload.load( + "/sprites/heart_note.bmp" + ) + # Make the background color transparent + self.heart_palette.make_transparent(0) + + # Load the Drum note bitmap for channel 3 notes + self.drum_note, self.drum_palette = adafruit_imageload.load( + "/sprites/drum_note.bmp" + ) + # Make the background color transparent + self.drum_palette.make_transparent(0) + + # Load the new sprites for channels 4, 5, and 6 + # Meatball for channel 4 + self.meatball_note, self.meatball_palette = adafruit_imageload.load( + "/sprites/meatball.bmp" + ) + self.meatball_palette.make_transparent(0) + + # Star for channel 5 + self.star_note, self.star_palette = adafruit_imageload.load( + "/sprites/star.bmp" + ) + self.star_palette.make_transparent(0) + + # Bot for channel 6 + self.bot_note, self.bot_palette = adafruit_imageload.load("/sprites/bot.bmp") + self.bot_palette.make_transparent(0) + + except Exception as e: + print(f"Error loading sprites: {e}") + + def create_palettes(self): + """Create palettes for notes and preview""" + # Create a palette for music notes with multiple colors + for channel_color in self.channel_colors: + palette = Palette(2) + palette[0] = self.bg_color # Transparent (sage green background) + palette[1] = channel_color # Note color for this channel + self.note_palettes.append(palette) + + # Create a preview palette with multiple colors + for channel_color in self.channel_colors: + palette = Palette(2) + palette[0] = self.bg_color # Transparent (sage green background) + # For preview, use a lighter version of the channel color + r = ((channel_color >> 16) & 0xFF) // 2 + 0x40 + g = ((channel_color >> 8) & 0xFF) // 2 + 0x40 + b = (channel_color & 0xFF) // 2 + 0x40 + preview_color = (r << 16) | (g << 8) | b + palette[1] = preview_color + self.preview_palettes.append(palette) + + def create_preview_note(self, current_channel, note_bitmap): + """Create preview note based on channel""" + if current_channel == 0: # Channel 1 uses Lars note + preview_tg = TileGrid(self.mario_head, pixel_shader=self.mario_palette) + elif current_channel == 1: # Channel 2 uses Heart note + preview_tg = TileGrid(self.heart_note, pixel_shader=self.heart_palette) + elif current_channel == 2: # Channel 3 uses Drum note + preview_tg = TileGrid(self.drum_note, pixel_shader=self.drum_palette) + elif current_channel == 3: # Channel 4 uses Meatball + preview_tg = TileGrid(self.meatball_note, pixel_shader=self.meatball_palette) + elif current_channel == 4: # Channel 5 uses Star + preview_tg = TileGrid(self.star_note, pixel_shader=self.star_palette) + elif current_channel == 5: # Channel 6 uses Bot + preview_tg = TileGrid(self.bot_note, pixel_shader=self.bot_palette) + else: # Fallback to colored circle + preview_tg = TileGrid( + note_bitmap, + pixel_shader=self.preview_palettes[current_channel] + ) + + preview_tg.x = 0 + preview_tg.y = 0 + preview_tg.hidden = True # Start with preview hidden + + return preview_tg + + def load_button_sprites(self): + """Load button sprites for transport controls""" + try: + # Load play button images + self.play_up, self.play_up_palette = adafruit_imageload.load( + "/sprites/play_up.bmp" + ) + self.play_up_palette.make_transparent(0) + + self.play_down, self.play_down_palette = adafruit_imageload.load( + "/sprites/play_down.bmp" + ) + self.play_down_palette.make_transparent(0) + + # Load stop button images + self.stop_up, self.stop_up_palette = adafruit_imageload.load( + "/sprites/stop_up.bmp" + ) + self.stop_up_palette.make_transparent(0) + + self.stop_down, self.stop_down_palette = adafruit_imageload.load( + "/sprites/stop_down.bmp" + ) + self.stop_down_palette.make_transparent(0) + + # Load loop button images + self.loop_up, self.loop_up_palette = adafruit_imageload.load( + "/sprites/loop_up.bmp" + ) + self.loop_up_palette.make_transparent(0) + + self.loop_down, self.loop_down_palette = adafruit_imageload.load( + "/sprites/loop_down.bmp" + ) + self.loop_down_palette.make_transparent(0) + + # Load clear button images + self.clear_up, self.clear_up_palette = adafruit_imageload.load( + "/sprites/clear_up.bmp" + ) + self.clear_up_palette.make_transparent(0) + + self.clear_down, self.clear_down_palette = adafruit_imageload.load( + "/sprites/clear_down.bmp" + ) + self.clear_down_palette.make_transparent(0) + + return True + except Exception as e: + print(f"Error loading button sprites: {e}") + return False diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/bot.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/bot.bmp new file mode 100755 index 000000000..d409f1fa2 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/bot.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/clear_down.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/clear_down.bmp new file mode 100755 index 000000000..248c261e5 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/clear_down.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/clear_up.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/clear_up.bmp new file mode 100755 index 000000000..70fdb13c6 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/clear_up.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/drum_note.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/drum_note.bmp new file mode 100755 index 000000000..a01c582e2 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/drum_note.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/heart_note.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/heart_note.bmp new file mode 100755 index 000000000..c3c460cd7 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/heart_note.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/lars_note.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/lars_note.bmp new file mode 100755 index 000000000..0c1f3e873 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/lars_note.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/loop_down.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/loop_down.bmp new file mode 100755 index 000000000..297786d09 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/loop_down.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/loop_up.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/loop_up.bmp new file mode 100755 index 000000000..2d1a0aa86 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/loop_up.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/meatball.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/meatball.bmp new file mode 100755 index 000000000..6d93e0dd6 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/meatball.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/play_down.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/play_down.bmp new file mode 100755 index 000000000..077c05552 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/play_down.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/play_up.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/play_up.bmp new file mode 100755 index 000000000..d9e3e53a3 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/play_up.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/star.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/star.bmp new file mode 100755 index 000000000..18247a132 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/star.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/stop_down.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/stop_down.bmp new file mode 100755 index 000000000..44cf026ae Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/stop_down.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/sprites/stop_up.bmp b/Fruit_Jam/Larsio_Paint_Music/sprites/stop_up.bmp new file mode 100755 index 000000000..28b07b908 Binary files /dev/null and b/Fruit_Jam/Larsio_Paint_Music/sprites/stop_up.bmp differ diff --git a/Fruit_Jam/Larsio_Paint_Music/staff_view.py b/Fruit_Jam/Larsio_Paint_Music/staff_view.py new file mode 100755 index 000000000..75ea89388 --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/staff_view.py @@ -0,0 +1,220 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +# staff_view.py: Larsio Paint Music component +""" + +# pylint: disable=import-error, trailing-whitespace +from displayio import Group, Bitmap, Palette, TileGrid + + +# pylint: disable=invalid-name,no-member,too-many-instance-attributes,too-many-arguments +# pylint: disable=too-many-branches,too-many-statements,too-many-locals,too-many-nested-blocks +class StaffView: + """Manages the music staff display and related elements""" + + def __init__(self, screen_width, screen_height, note_manager): + self.SCREEN_WIDTH = screen_width + self.SCREEN_HEIGHT = screen_height + self.note_manager = note_manager + + # Staff dimensions + self.TOP_MARGIN = int(self.SCREEN_HEIGHT * 0.1) + self.BOTTOM_MARGIN = int(self.SCREEN_HEIGHT * 0.2) + self.STAFF_HEIGHT = int((self.SCREEN_HEIGHT - self.TOP_MARGIN - self.BOTTOM_MARGIN) * 0.95) + self.STAFF_Y_START = self.TOP_MARGIN + self.LINE_SPACING = self.STAFF_HEIGHT // 8 + + # Margins and spacing + self.START_MARGIN = 25 # Pixels from left edge for the double bar + + # Note spacing + self.EIGHTH_NOTE_SPACING = self.SCREEN_WIDTH // 40 + self.QUARTER_NOTE_SPACING = self.EIGHTH_NOTE_SPACING * 2 + + # Measure settings + self.NOTES_PER_MEASURE = 4 + self.MEASURE_WIDTH = self.QUARTER_NOTE_SPACING * self.NOTES_PER_MEASURE + self.MEASURES_PER_LINE = 4 + + # Playback elements + self.playhead = None + self.highlight_grid = None + + # X positions for notes + self.x_positions = [] + self._generate_x_positions() + + def _generate_x_positions(self): + """Generate horizontal positions for notes""" + self.x_positions = [] + for measure in range(self.MEASURES_PER_LINE): + measure_start = self.START_MARGIN + (measure * self.MEASURE_WIDTH) + for eighth_pos in range(8): + x_pos = (measure_start + (eighth_pos * self.EIGHTH_NOTE_SPACING) + + self.EIGHTH_NOTE_SPACING // 2) + if x_pos < self.SCREEN_WIDTH: + self.x_positions.append(x_pos) + + # Share positions with note manager + self.note_manager.x_positions = self.x_positions + + def create_staff(self): + """Create the staff with lines and background""" + staff_group = Group() + + # Create staff background + staff_bg_bitmap = Bitmap(self.SCREEN_WIDTH, self.STAFF_HEIGHT, 2) + staff_bg_palette = Palette(2) + staff_bg_palette[0] = 0xF5F5DC # Light beige (transparent) + staff_bg_palette[1] = 0x657c95 # 8AAD8A + + # Fill staff background with sage green + for x in range(self.SCREEN_WIDTH): + for y in range(self.STAFF_HEIGHT): + staff_bg_bitmap[x, y] = 1 + + # Create a TileGrid for staff background + staff_bg_grid = TileGrid( + staff_bg_bitmap, + pixel_shader=staff_bg_palette, + x=0, + y=self.STAFF_Y_START + ) + staff_group.append(staff_bg_grid) + + # Create staff lines + staff_bitmap = Bitmap(self.SCREEN_WIDTH, self.STAFF_HEIGHT, 4) + staff_palette = Palette(4) + staff_palette[0] = 0x657c95 # + staff_palette[1] = 0x000000 # Black for horizontal staff lines + staff_palette[2] = 0x888888 # Medium gray for measure bar lines + staff_palette[3] = 0xAAAAAA # Lighter gray for quarter note dividers + + # Draw 5 horizontal staff lines + for i in range(5): + y_pos = (i + 1) * self.LINE_SPACING + for x in range(self.SCREEN_WIDTH): + staff_bitmap[x, y_pos] = 1 + + # Add double bar at the beginning + for x in range(self.START_MARGIN - 5, self.START_MARGIN - 2): + for y in range(self.STAFF_HEIGHT): + if self.LINE_SPACING <= y <= 5 * self.LINE_SPACING: + staff_bitmap[x, y] = 1 + + for x in range(self.START_MARGIN - 1, self.START_MARGIN + 2): + for y in range(self.STAFF_HEIGHT): + if self.LINE_SPACING <= y <= 5 * self.LINE_SPACING: + staff_bitmap[x, y] = 1 + + # Add measure bar lines (thicker, darker) + bar_line_width = 2 + + # For each measure (except after the last one) + for i in range(1, self.MEASURES_PER_LINE): + # Calculate measure bar position + measure_bar_x = self.START_MARGIN + (i * self.MEASURE_WIDTH) + + if measure_bar_x < self.SCREEN_WIDTH: + # Draw the measure bar line + for y in range(self.STAFF_HEIGHT): + if self.LINE_SPACING <= y <= 5 * self.LINE_SPACING: + for thickness in range(bar_line_width): + if measure_bar_x + thickness < self.SCREEN_WIDTH: + staff_bitmap[measure_bar_x + thickness, y] = 2 + + # Add quarter note divider lines within each measure + for measure in range(self.MEASURES_PER_LINE): + measure_start_x = self.START_MARGIN + (measure * self.MEASURE_WIDTH) + + # Calculate quarter note positions (divide measure into 4 equal parts) + quarter_width = self.MEASURE_WIDTH // 4 + + # Draw lines at the first, second, and third quarter positions + for q in range(1, 4): # Draw at positions 1, 2, and 3 (not at 0 or 4) + quarter_x = measure_start_x + (q * quarter_width) + + if quarter_x < self.SCREEN_WIDTH: + for y in range(self.STAFF_HEIGHT): + if self.LINE_SPACING <= y <= 5 * self.LINE_SPACING: + staff_bitmap[quarter_x, y] = 3 # Use color 3 (light gray) + + # Add double bar line at the end + double_bar_width = 5 + double_bar_x = self.START_MARGIN + (self.MEASURES_PER_LINE * self.MEASURE_WIDTH) + 5 + if double_bar_x + double_bar_width < self.SCREEN_WIDTH: + # First thick line + for x in range(3): + for y in range(self.STAFF_HEIGHT): + if self.LINE_SPACING <= y <= 5 * self.LINE_SPACING: + staff_bitmap[double_bar_x + x, y] = 1 + + # Second thick line (with gap) + for x in range(3): + for y in range(self.STAFF_HEIGHT): + if self.LINE_SPACING <= y <= 5 * self.LINE_SPACING: + staff_bitmap[double_bar_x + x + 4, y] = 1 + + # Create a TileGrid with the staff bitmap + staff_grid = TileGrid( + staff_bitmap, + pixel_shader=staff_palette, + x=0, + y=self.STAFF_Y_START + ) + staff_group.append(staff_grid) + + return staff_group + + def create_grid_lines(self): + """Add vertical grid lines to show note spacing""" + grid_bitmap = Bitmap(self.SCREEN_WIDTH, self.STAFF_HEIGHT, 2) + grid_palette = Palette(2) + grid_palette[0] = 0x657c95 # Transparent + grid_palette[1] = 0xAAAAAA # Faint grid lines (light gray) + + # Draw vertical grid lines at each eighth note position + for x_pos in self.x_positions: + for y in range(self.STAFF_HEIGHT): + if self.LINE_SPACING <= y <= 5 * self.LINE_SPACING: + grid_bitmap[x_pos, y] = 1 + + return TileGrid(grid_bitmap, pixel_shader=grid_palette, x=0, y=self.STAFF_Y_START) + + def create_playhead(self): + """Create a playhead indicator""" + playhead_bitmap = Bitmap(2, self.STAFF_HEIGHT, 2) + playhead_palette = Palette(2) + playhead_palette[0] = 0x657c95 # Transparent + playhead_palette[1] = 0xFF0000 # Red playhead line + + for y in range(self.STAFF_HEIGHT): + playhead_bitmap[0, y] = 1 + playhead_bitmap[1, y] = 1 + + self.playhead = TileGrid( + playhead_bitmap, + pixel_shader=playhead_palette, + x=0, + y=self.STAFF_Y_START + ) + self.playhead.x = -10 # Start off-screen + + return self.playhead + + def create_highlight(self): + """Create a highlight marker for the closest valid note position""" + highlight_bitmap = Bitmap(self.SCREEN_WIDTH, 3, 2) + highlight_palette = Palette(2) + highlight_palette[0] = 0x657c95 # Transparent + highlight_palette[1] = 0x007700 # Highlight color (green) + + for x in range(self.SCREEN_WIDTH): + highlight_bitmap[x, 1] = 1 + + self.highlight_grid = TileGrid(highlight_bitmap, pixel_shader=highlight_palette) + self.highlight_grid.y = self.note_manager.note_positions[0] # Start at first position + + return self.highlight_grid diff --git a/Fruit_Jam/Larsio_Paint_Music/ui_manager.py b/Fruit_Jam/Larsio_Paint_Music/ui_manager.py new file mode 100755 index 000000000..00c953945 --- /dev/null +++ b/Fruit_Jam/Larsio_Paint_Music/ui_manager.py @@ -0,0 +1,644 @@ +# SPDX-FileCopyrightText: 2025 John Park and Claude AI for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +# ui_manager.py: CircuitPython Music Staff Application component +""" + +import time +import gc + +# pylint: disable=import-error, trailing-whitespace, line-too-long, superfluous-parens +from adafruit_display_text.bitmap_label import Label +import terminalio +from displayio import TileGrid + +from display_manager import DisplayManager +from staff_view import StaffView +from control_panel import ControlPanel +from input_handler import InputHandler +from sprite_manager import SpriteManager +from cursor_manager import CursorManager +from playback_controller import PlaybackController + + +# pylint: disable=invalid-name,no-member,too-many-instance-attributes,too-many-arguments +# pylint: disable=too-many-branches,too-many-statements,too-many-public-methods +# pylint: disable=too-many-locals,attribute-defined-outside-init +# pylint: disable=consider-using-in,too-many-return-statements,no-else-return +class UIManager: + """Manages the UI elements, input, and user interaction""" + + def __init__(self, sound_manager, note_manager): + """Initialize the UI manager with sound and note managers""" + self.sound_manager = sound_manager + self.note_manager = note_manager + + # Screen dimensions + self.SCREEN_WIDTH = 320 + self.SCREEN_HEIGHT = 240 + + # Staff dimensions + self.TOP_MARGIN = int(self.SCREEN_HEIGHT * 0.1) + self.BOTTOM_MARGIN = int(self.SCREEN_HEIGHT * 0.2) + self.STAFF_HEIGHT = int((self.SCREEN_HEIGHT - self.TOP_MARGIN - self.BOTTOM_MARGIN) * 0.95) + self.STAFF_Y_START = self.TOP_MARGIN + self.LINE_SPACING = self.STAFF_HEIGHT // 8 + + # Start margin + self.START_MARGIN = 25 + + # Tempo and timing + self.BPM = 120 + self.SECONDS_PER_BEAT = 60 / self.BPM + self.SECONDS_PER_EIGHTH = self.SECONDS_PER_BEAT / 2 + + # Initialize components + self.display_manager = DisplayManager(self.SCREEN_WIDTH, self.SCREEN_HEIGHT) + self.staff_view = StaffView(self.SCREEN_WIDTH, self.SCREEN_HEIGHT, self.note_manager) + self.control_panel = ControlPanel(self.SCREEN_WIDTH, self.SCREEN_HEIGHT) + self.input_handler = InputHandler(self.SCREEN_WIDTH, self.SCREEN_HEIGHT, + self.STAFF_Y_START, self.STAFF_HEIGHT) + self.sprite_manager = SpriteManager() + self.cursor_manager = CursorManager() + self.playback_controller = PlaybackController(self.sound_manager, self.note_manager, + self.SECONDS_PER_EIGHTH) + + # UI elements + self.main_group = None + self.note_name_label = None + self.tempo_label = None + self.preview_tg = None + self.highlight_grid = None + self.playhead = None + self.channel_buttons = [] + self.channel_selector = None + + # Initialize attributes that will be defined later + self.display = None + self.play_button = None + self.stop_button = None + self.loop_button = None + self.clear_button = None + self.crosshair_cursor = None + self.triangle_cursor = None + self.tempo_minus_label = None + self.tempo_plus_label = None + + # Channel setting + self.current_channel = 0 + + def setup_display(self): + """Initialize the display and create visual elements""" + # Initialize display + self.main_group, self.display = self.display_manager.initialize_display() + + # Create background + bg_grid = self.display_manager.create_background() + self.main_group.append(bg_grid) + + # Create staff + staff_group = self.staff_view.create_staff() + self.main_group.append(staff_group) + + # Create grid lines + grid_tg = self.staff_view.create_grid_lines() + self.main_group.insert(1, grid_tg) # Insert before staff so it appears behind + + # Create channel buttons using sprites + self._create_sprite_channel_buttons() + + # Create transport controls + transport_group, self.play_button, self.stop_button, self.loop_button, self.clear_button = \ + self.control_panel.create_transport_controls(self.sprite_manager) + self.main_group.append(transport_group) + + # Create cursors + self.crosshair_cursor, self.triangle_cursor = self.cursor_manager.create_cursors() + self.main_group.append(self.crosshair_cursor) + self.main_group.append(self.triangle_cursor) + + # Create note name label + self._create_note_name_label() + + # Create tempo display + self._create_tempo_display() + + # Create highlight + self.highlight_grid = self.staff_view.create_highlight() + self.main_group.append(self.highlight_grid) + + # Create playhead + self.playhead = self.staff_view.create_playhead() + self.main_group.append(self.playhead) + + # Set playback controller elements + self.playback_controller.set_ui_elements( + self.playhead, + self.play_button, + self.stop_button, + self.control_panel.button_sprites + ) + + # Create preview note + self.preview_tg = self.sprite_manager.create_preview_note( + self.current_channel, self.note_manager.note_bitmap) + self.main_group.append(self.preview_tg) + + # Add note groups to main group + self.main_group.append(self.note_manager.notes_group) + self.main_group.append(self.note_manager.ledger_lines_group) + + def _create_sprite_channel_buttons(self): + """Create channel buttons using sprites instead of numbered boxes""" + # Get a reference to the channel selector from control panel + channel_group, self.channel_selector = self.control_panel.create_channel_buttons() + + # Add sprite-based channel buttons + button_sprites = [ + (self.sprite_manager.mario_head, self.sprite_manager.mario_palette), + (self.sprite_manager.heart_note, self.sprite_manager.heart_palette), + (self.sprite_manager.drum_note, self.sprite_manager.drum_palette), + (self.sprite_manager.meatball_note, self.sprite_manager.meatball_palette), + (self.sprite_manager.star_note, self.sprite_manager.star_palette), + (self.sprite_manager.bot_note, self.sprite_manager.bot_palette) + ] + + # Create and position the sprite buttons + self.channel_buttons = [] + + for i, (sprite, palette) in enumerate(button_sprites): + button_x = 10 + i * (self.control_panel.CHANNEL_BUTTON_SIZE + + self.control_panel.CHANNEL_BUTTON_SPACING) + + # Create TileGrid for the sprite + button_tg = TileGrid( + sprite, + pixel_shader=palette, + x=button_x, + y=self.control_panel.CHANNEL_BUTTON_Y + ) + + # Center the sprite if it's not exactly the button size + if sprite.width != self.control_panel.CHANNEL_BUTTON_SIZE: + offset_x = (self.control_panel.CHANNEL_BUTTON_SIZE - sprite.width) // 2 + button_tg.x += offset_x + + if sprite.height != self.control_panel.CHANNEL_BUTTON_SIZE: + offset_y = (self.control_panel.CHANNEL_BUTTON_SIZE - sprite.height) // 2 + button_tg.y += offset_y + + self.channel_buttons.append(button_tg) + channel_group.append(button_tg) + + # Add the channel_group to main_group + self.main_group.append(channel_group) + + def _create_note_name_label(self): + """Create a label to show the current note name""" + self.note_name_label = Label( + terminalio.FONT, + text="", + color=0x000000, # Black text for beige background + scale=1 + ) + self.note_name_label.anchor_point = (0, 0) + self.note_name_label.anchored_position = (10, self.SCREEN_HEIGHT - 70) + self.main_group.append(self.note_name_label) + + def _create_tempo_display(self): + """Create a label for the tempo display with + and - buttons""" + gc.collect() # Force garbage collection before creating the label + + # Create plus and minus buttons for tempo adjustment + self.tempo_minus_label = Label( + terminalio.FONT, + text="-", + color=0xaaaaaa, # White text + background_color=0x444444, # Dark gray background + scale=1 + ) + self.tempo_minus_label.anchor_point = (0.5, 0.5) + self.tempo_minus_label.anchored_position = (self.SCREEN_WIDTH - 24, 10) + self.main_group.append(self.tempo_minus_label) + + self.tempo_plus_label = Label( + terminalio.FONT, + text="+", + color=0xaaaaaa, # gray text + background_color=0x444444, # Dark gray background + scale=1 + ) + self.tempo_plus_label.anchor_point = (0.5, 0.5) + self.tempo_plus_label.anchored_position = (self.SCREEN_WIDTH - 7, 10) + self.main_group.append(self.tempo_plus_label) + + # Create the tempo display label + self.tempo_label = Label( + terminalio.FONT, + text=f"Tempo~ {self.BPM} BPM", + color=0x222222, # gray text + scale=1 + ) + self.tempo_label.anchor_point = (0, 0.5) + self.tempo_label.anchored_position = (self.SCREEN_WIDTH - 114, 10) + self.main_group.append(self.tempo_label) + + print(f"Created tempo display: {self.tempo_label.text}") + + def find_mouse(self): + """Find the mouse device""" + return self.input_handler.find_mouse() + + def change_channel(self, channel_idx): + """Change the current MIDI channel""" + if 0 <= channel_idx < 6: # Ensure valid channel index + self.current_channel = channel_idx + + # Update channel selector position + channel_offset = (self.control_panel.CHANNEL_BUTTON_SIZE + + self.control_panel.CHANNEL_BUTTON_SPACING) + self.channel_selector.x = 7 + channel_idx * channel_offset + + # Update preview note color/image based on channel + self.main_group.remove(self.preview_tg) + self.preview_tg = self.sprite_manager.create_preview_note( + self.current_channel, self.note_manager.note_bitmap) + self.main_group.append(self.preview_tg) + + # Update status text + channel_names = ["Lars", "Heart", "Drum", "Meatball", "Star", "Bot"] + channel_text = f"Channel {self.current_channel + 1}: {channel_names[self.current_channel]}" + self.note_name_label.text = f"{channel_text} selected" + + print(f"Changed to MIDI channel {self.current_channel + 1}") + + def toggle_loop(self): + """Toggle loop button state""" + self.playback_controller.loop_enabled = not self.playback_controller.loop_enabled + self.control_panel.loop_enabled = self.playback_controller.loop_enabled + + # Update loop button appearance using bitmap if button_sprites are available + if hasattr(self.control_panel, 'button_sprites') and self.control_panel.button_sprites is not None: + state = 'down' if self.playback_controller.loop_enabled else 'up' + loop_bitmap, loop_palette = self.control_panel.button_sprites['loop'][state] + self.loop_button.bitmap = loop_bitmap + self.loop_button.pixel_shader = loop_palette + else: + # Fallback to original implementation + for x in range(1, self.control_panel.BUTTON_WIDTH - 1): + for y in range(1, self.control_panel.BUTTON_HEIGHT - 1): + skip_corners = (x, y) in [ + (0, 0), + (0, self.control_panel.BUTTON_HEIGHT-1), + (self.control_panel.BUTTON_WIDTH-1, 0), + (self.control_panel.BUTTON_WIDTH-1, self.control_panel.BUTTON_HEIGHT-1) + ] + + if not skip_corners: + # Skip pixels that are part of the loop symbol + dx = x - self.control_panel.BUTTON_WIDTH // 2 + dy = y - self.control_panel.BUTTON_HEIGHT // 2 + # Is pixel on the circle outline? + is_on_circle = (self.control_panel.loop_radius - 1 <= + (dx*dx + dy*dy)**0.5 <= + self.control_panel.loop_radius + 1) + + # Calculate arrow point positions + arrow_y1 = (self.control_panel.BUTTON_HEIGHT // 2 - + self.control_panel.loop_radius - 1) + arrow_y2 = arrow_y1 + 2 + + # Is pixel part of the arrow? + arrow_x = (self.control_panel.BUTTON_WIDTH // 2 + + int(self.control_panel.loop_radius * 0.7)) + is_arrow = x == arrow_x and (y == arrow_y1 or y == arrow_y2) + + if not (is_on_circle or is_arrow): + # Fill with active color if loop enabled, else inactive + val = 2 if self.playback_controller.loop_enabled else 0 + self.control_panel.loop_button_bitmap[x, y] = val + + self.note_name_label.text = "Loop: " + ("ON" if self.playback_controller.loop_enabled else "OFF") + + def press_clear_button(self): + """Handle clear button pressing effect""" + # Show pressed state + if hasattr(self.control_panel, 'button_sprites') and self.control_panel.button_sprites is not None: + self.clear_button.bitmap = self.control_panel.button_sprites['clear']['down'][0] + self.clear_button.pixel_shader = self.control_panel.button_sprites['clear']['down'][1] + else: + # Fallback to original implementation + for x in range(1, self.control_panel.BUTTON_WIDTH - 1): + for y in range(1, self.control_panel.BUTTON_HEIGHT - 1): + self.control_panel.clear_button_bitmap[x, y] = 2 # Red + + # Small delay for visual feedback + time.sleep(0.1) + + # Return to up state + if hasattr(self.control_panel, 'button_sprites') and self.control_panel.button_sprites is not None: + self.clear_button.bitmap = self.control_panel.button_sprites['clear']['up'][0] + self.clear_button.pixel_shader = self.control_panel.button_sprites['clear']['up'][1] + else: + # Fallback to original implementation + for x in range(1, self.control_panel.BUTTON_WIDTH - 1): + for y in range(1, self.control_panel.BUTTON_HEIGHT - 1): + self.control_panel.clear_button_bitmap[x, y] = 0 # Gray + + def clear_all_notes(self): + """Clear all notes""" + # Stop playback if it's running + if self.playback_controller.is_playing: + self.playback_controller.stop_playback() + + # Visual feedback for button press + self.press_clear_button() + + # Clear notes using note manager + self.note_manager.clear_all_notes(self.sound_manager) + + self.note_name_label.text = "All notes cleared" + + def adjust_tempo(self, direction): + """Adjust the tempo based on button press""" + # direction should be +1 for increase, -1 for decrease + + # Adjust BPM + new_bpm = self.BPM + (direction * 5) # Change by 5 BPM increments + + # Constrain to valid range + new_bpm = max(40, min(280, new_bpm)) + + # Only update if changed + if new_bpm != self.BPM: + self.BPM = new_bpm + self.SECONDS_PER_BEAT = 60 / self.BPM + self.SECONDS_PER_EIGHTH = self.SECONDS_PER_BEAT / 2 + + # Update playback controller with new tempo + self.playback_controller.set_tempo(self.SECONDS_PER_EIGHTH) + + # Update display + self.tempo_label.text = f"Tempo~ {self.BPM} BPM" + + print(f"Tempo adjusted to {self.BPM} BPM") + + def handle_mouse_position(self): + """Handle mouse movement and cursor updates""" + mouse_x = self.input_handler.mouse_x + mouse_y = self.input_handler.mouse_y + + # Check if mouse is over channel buttons area + is_over_channel_buttons = ( + self.control_panel.CHANNEL_BUTTON_Y <= mouse_y <= + self.control_panel.CHANNEL_BUTTON_Y + self.control_panel.CHANNEL_BUTTON_SIZE + ) + + # Check if we're over the staff area or transport controls area + is_over_staff = self.input_handler.is_over_staff(mouse_y) + is_over_transport = (mouse_y >= self.control_panel.TRANSPORT_AREA_Y) + + # Switch cursor based on area + self.cursor_manager.switch_cursor(use_triangle=(is_over_transport or is_over_channel_buttons)) + self.cursor_manager.set_cursor_position(mouse_x, mouse_y) + + # Handle staff area differently from other areas + if not is_over_staff: + # Hide highlight and preview when not over staff + self.highlight_grid.hidden = True + self.preview_tg.hidden = True + + # Show channel info if over channel buttons + if is_over_channel_buttons: + self._update_channel_button_info(mouse_x, mouse_y) + return + + # Process staff area interactions + # Find closest position and update highlight + closest_pos = self.note_manager.find_closest_position(mouse_y) + y_position = self.note_manager.note_positions[closest_pos] + self.highlight_grid.y = y_position - 1 # Center the highlight + self.highlight_grid.hidden = False + + # Find closest horizontal position (enforce minimum x position) + x_position = self.note_manager.find_closest_x_position(mouse_x) + + # Define sprite dimensions for each channel + sprite_width, sprite_height = self._get_sprite_dimensions(self.current_channel) + + # Update preview note position + self.preview_tg.x = x_position - sprite_width // 2 + self.preview_tg.y = y_position - sprite_height // 2 + self.preview_tg.hidden = False + + # Update note name label + if x_position < self.START_MARGIN: + self.note_name_label.text = "Invalid position - after double bar only" + else: + channel_names = ["Lars", "Heart", "Drum", "Meatball", "Star", "Bot"] + channel_text = f"Ch{self.current_channel+1} ({channel_names[self.current_channel]})" + note_text = self.note_manager.note_names[closest_pos] + self.note_name_label.text = f"{channel_text}: {note_text}" + + def _update_channel_button_info(self, mouse_x, mouse_y): + """Update the note name label based on which channel button the mouse is over""" + # Calculate which channel button we're over (if any) + for i in range(6): + button_x = 10 + i * (self.control_panel.CHANNEL_BUTTON_SIZE + + self.control_panel.CHANNEL_BUTTON_SPACING) + + # Get sprite dimensions for hit testing + sprite_width, sprite_height = self._get_sprite_dimensions(i) + + # Calculate the centered position of the sprite + offset_x = (self.control_panel.CHANNEL_BUTTON_SIZE - sprite_width) // 2 + offset_y = (self.control_panel.CHANNEL_BUTTON_SIZE - sprite_height) // 2 + sprite_x = button_x + offset_x + sprite_y = self.control_panel.CHANNEL_BUTTON_Y + offset_y + + # Check if mouse is over the sprite + rect_check = self.input_handler.point_in_rect( + mouse_x, mouse_y, sprite_x, sprite_y, + sprite_width, sprite_height) + + if rect_check: + channel_names = ["Lars", "Heart", "Drum", "Meatball", "Star", "Bot"] + self.note_name_label.text = f"Channel {i+1}: {channel_names[i]}" + break + + def _get_sprite_dimensions(self, channel_idx): + """Get the width and height of a sprite based on channel index""" + if channel_idx == 0: + return self.sprite_manager.mario_head.width, self.sprite_manager.mario_head.height + if channel_idx == 1: + return self.sprite_manager.heart_note.width, self.sprite_manager.heart_note.height + if channel_idx == 2: + return self.sprite_manager.drum_note.width, self.sprite_manager.drum_note.height + if channel_idx == 3: + return self.sprite_manager.meatball_note.width, self.sprite_manager.meatball_note.height + if channel_idx == 4: + return self.sprite_manager.star_note.width, self.sprite_manager.star_note.height + if channel_idx == 5: + return self.sprite_manager.bot_note.width, self.sprite_manager.bot_note.height + # Default fallback if channel_idx is out of range + return self.note_manager.NOTE_WIDTH, self.note_manager.NOTE_HEIGHT + + def handle_mouse_buttons(self): + """Handle mouse button presses""" + mouse_x = self.input_handler.mouse_x + mouse_y = self.input_handler.mouse_y + + # Check for staff area + is_over_staff = self.input_handler.is_over_staff(mouse_y) + + if self.input_handler.left_button_pressed: + # Check for tempo button clicks + minus_button_x, minus_button_y = self.tempo_minus_label.anchored_position + plus_button_x, plus_button_y = self.tempo_plus_label.anchored_position + button_radius = 8 # Allow a bit of space around the button for easier clicking + + if ((mouse_x - minus_button_x)**2 + (mouse_y - minus_button_y)**2) < button_radius**2: + # Clicked minus button - decrease tempo + self.adjust_tempo(-1) + return + + if ((mouse_x - plus_button_x)**2 + (mouse_y - plus_button_y)**2) < button_radius**2: + # Clicked plus button - increase tempo + self.adjust_tempo(1) + return + + # Check if a channel button was clicked + channel_clicked = False + for i in range(6): + button_x = 10 + i * (self.control_panel.CHANNEL_BUTTON_SIZE + + self.control_panel.CHANNEL_BUTTON_SPACING) + + # Get sprite dimensions for hit testing + sprite_width, sprite_height = self._get_sprite_dimensions(i) + + # Calculate the centered position of the sprite + offset_x = (self.control_panel.CHANNEL_BUTTON_SIZE - sprite_width) // 2 + offset_y = (self.control_panel.CHANNEL_BUTTON_SIZE - sprite_height) // 2 + sprite_x = button_x + offset_x + sprite_y = self.control_panel.CHANNEL_BUTTON_Y + offset_y + + # Check if click is within the sprite area + if self.input_handler.point_in_rect( + mouse_x, mouse_y, sprite_x, sprite_y, + sprite_width, sprite_height): + self.change_channel(i) + channel_clicked = True + break + + if not channel_clicked: + # Handle play/stop button clicks + if self.input_handler.point_in_rect( + mouse_x, mouse_y, self.play_button.x, self.play_button.y, + self.control_panel.BUTTON_WIDTH, self.control_panel.BUTTON_HEIGHT): + if not self.playback_controller.is_playing: + self.playback_controller.start_playback(self.START_MARGIN) + else: + self.playback_controller.stop_playback() + elif self.input_handler.point_in_rect( + mouse_x, mouse_y, self.stop_button.x, self.stop_button.y, + self.control_panel.BUTTON_WIDTH, self.control_panel.BUTTON_HEIGHT): + self.playback_controller.stop_playback() + elif self.input_handler.point_in_rect( + mouse_x, mouse_y, self.loop_button.x, self.loop_button.y, + self.control_panel.BUTTON_WIDTH, self.control_panel.BUTTON_HEIGHT): + self.toggle_loop() + elif self.input_handler.point_in_rect( + mouse_x, mouse_y, self.clear_button.x, self.clear_button.y, + self.control_panel.BUTTON_WIDTH, self.control_panel.BUTTON_HEIGHT): + self.clear_all_notes() + # Handle staff area clicks - left button adds notes only + elif is_over_staff: + self._add_note_based_on_channel(mouse_x, mouse_y) + + # Handle right mouse button for note deletion + elif self.input_handler.right_button_pressed and is_over_staff: + _, message = self.note_manager.erase_note( + mouse_x, mouse_y, + self.sprite_manager.mario_head, self.sprite_manager.mario_palette, + self.sound_manager + ) + self.note_name_label.text = message + + def _add_note_based_on_channel(self, x, y): + """Add a note based on the current channel""" + if self.current_channel == 0: + _, message = self.note_manager.add_note( + x, y, self.current_channel, + self.sprite_manager.note_palettes, + self.sprite_manager.mario_head, self.sprite_manager.mario_palette, + self.sprite_manager.heart_note, self.sprite_manager.heart_palette, + self.sound_manager + ) + elif self.current_channel == 1: + _, message = self.note_manager.add_note( + x, y, self.current_channel, + self.sprite_manager.note_palettes, + self.sprite_manager.heart_note, self.sprite_manager.heart_palette, + self.sprite_manager.heart_note, self.sprite_manager.heart_palette, + self.sound_manager + ) + elif self.current_channel == 2: + _, message = self.note_manager.add_note( + x, y, self.current_channel, + self.sprite_manager.note_palettes, + self.sprite_manager.drum_note, self.sprite_manager.drum_palette, + self.sprite_manager.heart_note, self.sprite_manager.heart_palette, + self.sound_manager + ) + elif self.current_channel == 3: + _, message = self.note_manager.add_note( + x, y, self.current_channel, + self.sprite_manager.note_palettes, + self.sprite_manager.meatball_note, self.sprite_manager.meatball_palette, + self.sprite_manager.heart_note, self.sprite_manager.heart_palette, + self.sound_manager + ) + elif self.current_channel == 4: + _, message = self.note_manager.add_note( + x, y, self.current_channel, + self.sprite_manager.note_palettes, + self.sprite_manager.star_note, self.sprite_manager.star_palette, + self.sprite_manager.heart_note, self.sprite_manager.heart_palette, + self.sound_manager + ) + elif self.current_channel == 5: + _, message = self.note_manager.add_note( + x, y, self.current_channel, + self.sprite_manager.note_palettes, + self.sprite_manager.bot_note, self.sprite_manager.bot_palette, + self.sprite_manager.heart_note, self.sprite_manager.heart_palette, + self.sound_manager + ) + else: + _, message = self.note_manager.add_note( + x, y, self.current_channel, + self.sprite_manager.note_palettes, + self.sprite_manager.mario_head, self.sprite_manager.mario_palette, + self.sprite_manager.heart_note, self.sprite_manager.heart_palette, + self.sound_manager + ) + self.note_name_label.text = message + + def main_loop(self): + """Main application loop""" + while True: + # Update playback if active + if self.playback_controller.is_playing: + self.playback_controller.update_playback(self.staff_view.x_positions) + + # Update sound manager for timed releases + self.sound_manager.update() + + # Process mouse input - simplified version without wheel tracking + if self.input_handler.process_mouse_input(): + # Handle mouse position and update cursor + self.handle_mouse_position() + + # Handle mouse button presses + self.handle_mouse_buttons() diff --git a/LED_Matrix_Clock/code.py b/LED_Matrix_Clock/code.py new file mode 100644 index 000000000..3244383fe --- /dev/null +++ b/LED_Matrix_Clock/code.py @@ -0,0 +1,468 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# SPDX-License-Identifier: MIT + +'''LED Matrix Alarm Clock with Scrolling Wake Up Text and Winking Eyes''' +import os +import ssl +import time +import random +import wifi +import socketpool +import microcontroller +import board +import audiocore +import audiobusio +import audiomixer +import adafruit_is31fl3741 +from adafruit_is31fl3741.adafruit_rgbmatrixqt import Adafruit_RGBMatrixQT +import adafruit_ntp +from adafruit_ticks import ticks_ms, ticks_add, ticks_diff +from rainbowio import colorwheel +from adafruit_seesaw import digitalio, rotaryio, seesaw +from adafruit_debouncer import Button + +# Configuration +timezone = -4 +alarm_hour = 11 +alarm_min = 36 +alarm_volume = .2 +hour_12 = True +no_alarm_plz = False +BRIGHTNESS_DAY = 200 +BRIGHTNESS_NIGHT = 50 + +# I2S pins for Audio BFF +DATA = board.A0 +LRCLK = board.A1 +BCLK = board.A2 + +# Connect to WIFI +wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")) +print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}") + +context = ssl.create_default_context() +pool = socketpool.SocketPool(wifi.radio) +ntp = adafruit_ntp.NTP(pool, tz_offset=timezone, cache_seconds=3600) + +# Initialize I2C and displays +i2c = board.STEMMA_I2C() +matrix1 = Adafruit_RGBMatrixQT(i2c, address=0x30, allocate=adafruit_is31fl3741.PREFER_BUFFER) +matrix2 = Adafruit_RGBMatrixQT(i2c, address=0x31, allocate=adafruit_is31fl3741.PREFER_BUFFER) + +# Configure displays +for m in [matrix1, matrix2]: + m.global_current = 0x05 + m.set_led_scaling(BRIGHTNESS_DAY) + m.enable = True + m.fill(0x000000) + m.show() + +# Audio setup +audio = audiobusio.I2SOut(BCLK, LRCLK, DATA) +wavs = ["/"+f for f in os.listdir('/') if f.lower().endswith('.wav') and not f.startswith('.')] +mixer = audiomixer.Mixer(voice_count=1, sample_rate=22050, channel_count=1, + bits_per_sample=16, samples_signed=True, buffer_size=32768) +mixer.voice[0].level = alarm_volume +audio.play(mixer) + +def open_audio(): + """Open a random WAV file""" + filename = random.choice(wavs) + return audiocore.WaveFile(open(filename, "rb")) + +def update_brightness(hour_24): + """Update LED brightness based on time of day""" + brightness = BRIGHTNESS_NIGHT if (hour_24 >= 20 or hour_24 < 7) else BRIGHTNESS_DAY + matrix1.set_led_scaling(brightness) + matrix2.set_led_scaling(brightness) + return brightness + +# Seesaw setup for encoder and button +seesaw = seesaw.Seesaw(i2c, addr=0x36) +seesaw.pin_mode(24, seesaw.INPUT_PULLUP) +button = Button(digitalio.DigitalIO(seesaw, 24), long_duration_ms=1000) +encoder = rotaryio.IncrementalEncoder(seesaw) +last_position = 0 + +# Font definitions +FONT_5X7 = { + '0': [0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110], + '1': [0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110], + '2': [0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111], + '3': [0b11111, 0b00010, 0b00100, 0b00010, 0b00001, 0b10001, 0b01110], + '4': [0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010], + '5': [0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110], + '6': [0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110], + '7': [0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000], + '8': [0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110], + '9': [0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100], + ' ': [0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000], + 'W': [0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b11011, 0b10001], + 'A': [0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001], + 'K': [0b10001, 0b10010, 0b10100, 0b11000, 0b10100, 0b10010, 0b10001], + 'E': [0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111], + 'U': [0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110], + 'P': [0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000], + 'O': [0b01110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b01110], + 'N': [0b10001, 0b11001, 0b10101, 0b10101, 0b10011, 0b10001, 0b10001], + 'F': [0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000] +} + +# Eye patterns +EYE_OPEN = [0b10101, 0b01110, 0b10001, 0b10101, 0b10001, 0b01110, 0b00000] +EYE_CLOSED = [0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000] + +class Display: + """Handle all display operations""" + def __init__(self, m1, m2): + self.matrix1 = m1 + self.matrix2 = m2 + + def clear(self): + """Clear both displays""" + self.matrix1.fill(0x000000) + self.matrix2.fill(0x000000) + + def show(self): + """Update both displays""" + self.matrix1.show() + self.matrix2.show() + + def pixel(self, matrix, x, y, color): # pylint: disable=no-self-use + """Draw a pixel with 180-degree rotation""" + fx, fy = 12 - x, 8 - y + if 0 <= fx < 13 and 0 <= fy < 9: + matrix.pixel(fx, fy, color) + + def draw_char(self, matrix, char, x, y, color): + """Draw a character at position x,y""" + if char.upper() in FONT_5X7: + bitmap = FONT_5X7[char.upper()] + for row in range(7): + for col in range(5): + if bitmap[row] & (1 << (4 - col)): + self.pixel(matrix, x + col, y + row, color) + + def draw_colon(self, y, color, is_pm=False): + """Draw colon split between displays with optional PM indicator""" + # Two dots for the colon + for dy in [(1, 2), (4, 5)]: + for offset in dy: + self.pixel(self.matrix1, 12, y + offset, color) + self.pixel(self.matrix2, 0, y + offset, color) + # PM indicator dot + if is_pm: + self.pixel(self.matrix1, 12, y + 6, color) + self.pixel(self.matrix2, 0, y + 6, color) + + def draw_time(self, time_str, color, is_pm=False): + """Draw time display across both matrices""" + self.clear() + y = 1 + # Draw digits + if len(time_str) >= 5: + self.draw_char(self.matrix1, time_str[0], 0, y, color) + self.draw_char(self.matrix1, time_str[1], 6, y, color) + self.draw_colon(y, color, is_pm) + self.draw_char(self.matrix2, time_str[3], 2, y, color) + self.draw_char(self.matrix2, time_str[4], 8, y, color) + self.show() + + def draw_scrolling_text(self, text, offset, color): + """Draw scrolling text across both matrices""" + self.clear() + char_width = 6 + total_width = 26 + # Calculate position for smooth scrolling + y = 1 + for i, char in enumerate(text): + # Start from right edge and move left + char_x = total_width - offset + (i * char_width) + # Draw character if any part is visible + if -6 < char_x < total_width: + if char_x < 13: # On matrix1 + self.draw_char(self.matrix1, char, char_x, y, color) + else: # On matrix2 + self.draw_char(self.matrix2, char, char_x - 13, y, color) + self.show() + + def draw_eye(self, matrix, pattern, color): + """Draw eye pattern centered on matrix""" + x, y = 4, 1 # Center position + for row in range(7): + for col in range(5): + if pattern[row] & (1 << (4 - col)): + self.pixel(matrix, x + col, y + row, color) + + def wink_animation(self, color): + """Perform winking animation""" + # Sequence: open -> left wink -> open -> right wink -> open + sequences = [ + (EYE_OPEN, EYE_OPEN), + (EYE_CLOSED, EYE_OPEN), + (EYE_OPEN, EYE_OPEN), + (EYE_OPEN, EYE_CLOSED), + (EYE_OPEN, EYE_OPEN) + ] + for left_eye, right_eye in sequences: + self.clear() + self.draw_eye(self.matrix1, left_eye, color) + self.draw_eye(self.matrix2, right_eye, color) + self.show() + time.sleep(0.3) + + def blink_time(self, time_str, color, is_pm=False, count=3): + """Blink time display for mode changes""" + for _ in range(count): + self.clear() + self.show() + time.sleep(0.2) + self.draw_time(time_str, color, is_pm) + time.sleep(0.2) + +# Initialize display handler +display = Display(matrix1, matrix2) + +# State variables +class State: + """Track all state variables""" + def __init__(self): + self.color_value = 0 + self.color = colorwheel(0) + self.is_pm = False + self.alarm_is_pm = False + self.time_str = "00:00" + self.set_alarm = 0 + self.active_alarm = False + self.alarm_str = f"{alarm_hour:02}:{alarm_min:02}" + self.current_brightness = BRIGHTNESS_DAY + # Timers + self.refresh_timer = Timer(3600000) # 1 hour + self.clock_timer = Timer(1000) # 1 second + self.wink_timer = Timer(30000) # 30 seconds + self.scroll_timer = Timer(80) # Scroll speed + self.blink_timer = Timer(500) # Blink speed + self.alarm_status_timer = Timer(100) # Status scroll + # Display state + self.scroll_offset = 0 + self.blink_state = True + self.showing_status = False + self.status_start_time = 0 + self.alarm_start_time = 0 + # Time tracking + self.first_run = True + self.seconds = 0 + self.mins = 0 + self.am_pm_hour = 0 + +class Timer: + """Simple timer helper""" + def __init__(self, interval): + self.interval = interval + self.last_tick = ticks_ms() + + def check(self): + """Check if timer has elapsed""" + if ticks_diff(ticks_ms(), self.last_tick) >= self.interval: + self.last_tick = ticks_add(self.last_tick, self.interval) + return True + return False + + def reset(self): + """Reset timer""" + self.last_tick = ticks_ms() + +# Initialize state +state = State() + +def format_time_display(hour_24, minute, use_12hr=True): + """Format time for display with AM/PM detection""" + if use_12hr: + hour = hour_24 % 12 + if hour == 0: + hour = 12 + is_pm = hour_24 >= 12 + else: + hour = hour_24 + is_pm = False + return f"{hour:02}:{minute:02}", is_pm + +def sync_time(): + """Sync with NTP server""" + try: + print("Getting time from internet!") + now = ntp.datetime + state.am_pm_hour = now.tm_hour + state.mins = now.tm_min + state.seconds = now.tm_sec + state.time_str, state.is_pm = format_time_display(state.am_pm_hour, state.mins, hour_12) + update_brightness(state.am_pm_hour) + if not state.active_alarm and not state.showing_status: + display.draw_time(state.time_str, state.color, state.is_pm) + print(f"Time: {state.time_str}") + state.first_run = False + return True + except Exception as e: # pylint: disable=broad-except + print(f"Error syncing time: {e}") + return False + +# Main loop +while True: + button.update() + + # Handle button presses + if button.long_press: + if state.set_alarm == 0 and not state.active_alarm: + # Enter alarm setting mode + state.blink_timer.reset() + state.set_alarm = 1 + state.alarm_is_pm = alarm_hour >= 12 if hour_12 else False + hour_str, _ = format_time_display(alarm_hour, 0, hour_12) + display.blink_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm) + # Draw the alarm hour after blinking to keep it displayed + display.draw_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm) + elif state.active_alarm: + # Stop alarm + mixer.voice[0].stop() + state.active_alarm = False + update_brightness(state.am_pm_hour) + state.scroll_offset = 0 + # Immediately redraw the current time + display.draw_time(state.time_str, state.color, state.is_pm) + print("Alarm silenced") + + if button.short_count == 1: # Changed from == 1 to >= 1 for better detection + # Cycle through alarm setting modes + state.set_alarm = (state.set_alarm + 1) % 3 + if state.set_alarm == 0: + # Exiting alarm setting mode - redraw current time + state.wink_timer.reset() + display.draw_time(state.time_str, state.color, state.is_pm) + elif state.set_alarm == 1: + # Entering hour setting + hour_str, _ = format_time_display(alarm_hour, 0, hour_12) + display.draw_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm) + # Reset timer to prevent immediate blinking + elif state.set_alarm == 2: + # Entering minute setting + display.blink_time(f" :{alarm_min:02}", state.color, state.alarm_is_pm) + # Draw the minutes after blinking to keep them displayed + display.draw_time(f" :{alarm_min:02}", state.color, state.alarm_is_pm) + # Reset timer to prevent immediate blinking + + if button.short_count == 3: # Changed for better detection + # Toggle alarm on/off + no_alarm_plz = not no_alarm_plz + print(f"Alarm disabled: {no_alarm_plz}") + state.showing_status = True + state.status_start_time = ticks_ms() + state.scroll_offset = 0 + + # Handle encoder (your existing code) + position = -encoder.position + if position != last_position: + delta = 1 if position > last_position else -1 + if state.set_alarm == 0: + # Change color + state.color_value = (state.color_value + delta * 5) % 255 + state.color = colorwheel(state.color_value) + display.draw_time(state.time_str, state.color, state.is_pm) + elif state.set_alarm == 1: + # Change hour + alarm_hour = (alarm_hour + delta) % 24 + state.alarm_is_pm = alarm_hour >= 12 if hour_12 else False + hour_str, _ = format_time_display(alarm_hour, 0, hour_12) + display.draw_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm) + elif state.set_alarm == 2: + # Change minute + alarm_min = (alarm_min + delta) % 60 + display.draw_time(f" :{alarm_min:02}", state.color, state.alarm_is_pm) + state.alarm_str = f"{alarm_hour:02}:{alarm_min:02}" + last_position = position + + # Handle alarm status display + if state.showing_status: + if state.alarm_status_timer.check(): + status_text = "OFF " if no_alarm_plz else "ON " + display.draw_scrolling_text(status_text, state.scroll_offset, state.color) + text_width = 4*6 if no_alarm_plz else 3*6 + state.scroll_offset += 1 + # Reset when text has completely scrolled off + if state.scroll_offset > text_width + 18: + state.scroll_offset = 0 + state.showing_status = False + if state.set_alarm == 0 and not state.active_alarm: + display.draw_time(state.time_str, state.color, state.is_pm) + + # Handle active alarm scrolling + if state.active_alarm: + # Auto-silence alarm after 1 minute + if ticks_diff(ticks_ms(), state.alarm_start_time) >= 60000: + mixer.voice[0].stop() + state.active_alarm = False + update_brightness(state.am_pm_hour) + state.scroll_offset = 0 + display.draw_time(state.time_str, state.color, state.is_pm) + print("Alarm auto-silenced") + elif state.scroll_timer.check(): + display.draw_scrolling_text("WAKE UP ", state.scroll_offset, state.color) + text_width = 8 * 6 # "WAKE UP " is 8 characters + state.scroll_offset += 1 + # Reset when text has completely scrolled off + if state.scroll_offset > text_width + 26: + state.scroll_offset = 0 + + # Handle alarm setting mode blinking + elif state.set_alarm > 0: + # Only blink if enough time has passed since mode change + if state.blink_timer.check(): + state.blink_state = not state.blink_state + if state.blink_state: + # Redraw during the "on" part of blink + if state.set_alarm == 1: + hour_str, _ = format_time_display(alarm_hour, 0, hour_12) + display.draw_time(hour_str[:2] + ": ", state.color, state.alarm_is_pm) + else: + display.draw_time(f" :{alarm_min:02}", state.color, state.alarm_is_pm) + else: + # Only clear display during the "off" part of blink + display.clear() + display.show() + + # Normal mode operations + else: # state.set_alarm == 0 + # Winking animation + if not state.active_alarm and not state.showing_status and state.wink_timer.check(): + print("Winking!") + display.wink_animation(state.color) + display.draw_time(state.time_str, state.color, state.is_pm) + + # Time sync + if state.refresh_timer.check() or state.first_run: + if not sync_time(): + time.sleep(10) + microcontroller.reset() + + # Local timekeeping + if state.clock_timer.check(): + state.seconds += 1 + if state.seconds > 59: + state.seconds = 0 + state.mins += 1 + if state.mins > 59: + state.mins = 0 + state.am_pm_hour = (state.am_pm_hour + 1) % 24 + update_brightness(state.am_pm_hour) + # Update display + state.time_str, state.is_pm = format_time_display(state.am_pm_hour, + state.mins, hour_12) + if not state.active_alarm and not state.showing_status: + display.draw_time(state.time_str, state.color, state.is_pm) + # Check alarm + if f"{state.am_pm_hour:02}:{state.mins:02}" == state.alarm_str and not no_alarm_plz: + print("ALARM!") + wave = open_audio() + mixer.voice[0].play(wave, loop=True) + state.active_alarm = True + state.alarm_start_time = ticks_ms() + state.scroll_offset = 0 diff --git a/LED_Matrix_Clock/nice-alarm.wav b/LED_Matrix_Clock/nice-alarm.wav new file mode 100644 index 000000000..50718c738 Binary files /dev/null and b/LED_Matrix_Clock/nice-alarm.wav differ diff --git a/LED_Matrix_Clock/square-alarm.wav b/LED_Matrix_Clock/square-alarm.wav new file mode 100644 index 000000000..92a54e503 Binary files /dev/null and b/LED_Matrix_Clock/square-alarm.wav differ diff --git a/MEMENTO/Memento_Face_Detect_Recognize/memento_platformio_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.cpp b/MEMENTO/Memento_Face_Detect_Recognize/memento_platformio_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.cpp index df87bc6f9..8b9bf28ef 100644 --- a/MEMENTO/Memento_Face_Detect_Recognize/memento_platformio_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.cpp +++ b/MEMENTO/Memento_Face_Detect_Recognize/memento_platformio_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.cpp @@ -172,7 +172,7 @@ bool Adafruit_PyCamera::initSD(void) { } Serial.println("Card successfully initialized"); - uint32_t size = sd.card()->cardSize(); + uint32_t size = sd.card()->sectorCount(); if (size == 0) { Serial.println("Can't determine the card size"); } else { @@ -497,7 +497,7 @@ bool Adafruit_PyCamera::takePhoto(const char *filename_base, return false; } - if (!sd.card() || (sd.card()->cardSize() == 0)) { + if (!sd.card() || (sd.card()->sectorCount() == 0)) { Serial.println("No SD card found"); // try to initialize? if (!initSD()) diff --git a/MEMENTO/Memento_Face_Detect_Recognize/memento_platformio_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.h b/MEMENTO/Memento_Face_Detect_Recognize/memento_platformio_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.h index 00863c10b..932d2bea1 100644 --- a/MEMENTO/Memento_Face_Detect_Recognize/memento_platformio_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.h +++ b/MEMENTO/Memento_Face_Detect_Recognize/memento_platformio_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.h @@ -6,7 +6,7 @@ #include #include #include // Hardware-specific library for ST7789 -#include +#include #ifndef TAG #define TAG "PYCAM" diff --git a/MEMENTO/Memento_Shoulder_Robot/platformio_memento_shoulder_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.cpp b/MEMENTO/Memento_Shoulder_Robot/platformio_memento_shoulder_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.cpp index df87bc6f9..8b9bf28ef 100644 --- a/MEMENTO/Memento_Shoulder_Robot/platformio_memento_shoulder_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.cpp +++ b/MEMENTO/Memento_Shoulder_Robot/platformio_memento_shoulder_camera/lib/Adafruit_PyCamera/Adafruit_PyCamera.cpp @@ -172,7 +172,7 @@ bool Adafruit_PyCamera::initSD(void) { } Serial.println("Card successfully initialized"); - uint32_t size = sd.card()->cardSize(); + uint32_t size = sd.card()->sectorCount(); if (size == 0) { Serial.println("Can't determine the card size"); } else { @@ -497,7 +497,7 @@ bool Adafruit_PyCamera::takePhoto(const char *filename_base, return false; } - if (!sd.card() || (sd.card()->cardSize() == 0)) { + if (!sd.card() || (sd.card()->sectorCount() == 0)) { Serial.println("No SD card found"); // try to initialize? if (!initSD()) diff --git a/Metro/Metro_RP2350_CircuitPython_Matrix/code.py b/Metro/Metro_RP2350_CircuitPython_Matrix/code.py index f6038f8ef..3e45bfe48 100644 --- a/Metro/Metro_RP2350_CircuitPython_Matrix/code.py +++ b/Metro/Metro_RP2350_CircuitPython_Matrix/code.py @@ -17,7 +17,9 @@ from tilepalettemapper import TilePaletteMapper from adafruit_fruitjam.peripherals import request_display_config import adafruit_imageload -request_display_config(320,240) + +# use the built-in HSTX display +request_display_config(320, 240) display = supervisor.runtime.display # screen size in tiles, tiles are 16x16 @@ -65,7 +67,12 @@ for i in range(0, len(COLORS)): shader_palette[i + 1] = COLORS[i] # mapper to change colors of tiles within the grid -grid_color_shader = TilePaletteMapper(shader_palette, 2, SCREEN_WIDTH, SCREEN_HEIGHT) +if sys.implementation.version[0] == 9: + grid_color_shader = TilePaletteMapper( + shader_palette, 2, SCREEN_WIDTH, SCREEN_HEIGHT + ) +elif sys.implementation.version[0] >= 10: + grid_color_shader = TilePaletteMapper(shader_palette, 2) # load the spritesheet katakana_bmp, katakana_pixelshader = adafruit_imageload.load("matrix_characters.bmp") diff --git a/Metro/Metro_RP2350_Match3/match3_game/code.py b/Metro/Metro_RP2350_Match3/match3_game/code.py index 3dd855aaf..2a1fb4692 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/code.py +++ b/Metro/Metro_RP2350_Match3/match3_game/code.py @@ -9,6 +9,7 @@ import array import atexit import io import os +import sys import time import board @@ -217,9 +218,11 @@ for i in range(2): # create tile palette mappers for i in range(2): - palette_mapper = TilePaletteMapper(remap_palette, 3, 1, 1) - # remap index 2 to each of the colors in mouse colors list - palette_mapper[0] = [0, 1, i + 3] + if sys.implementation.version[0] == 9: + palette_mapper = TilePaletteMapper(remap_palette, 3, 1, 1) + elif sys.implementation.version[0] >= 10: + palette_mapper = TilePaletteMapper(remap_palette, 3) + palette_mappers.append(palette_mapper) # create tilegrid for each mouse @@ -228,6 +231,9 @@ for i in range(2): mouse_tg.y = display.height // scale_factor // 2 mouse_tgs.append(mouse_tg) + # remap index 2 to each of the colors in mouse colors list + palette_mapper[0] = [0, 1, i + 3] + # USB info lists mouse_interface_indexes = [] mouse_endpoint_addresses = [] diff --git a/Metro/Metro_RP2350_Match3/match3_game/match3_game_helpers.py b/Metro/Metro_RP2350_Match3/match3_game/match3_game_helpers.py index fe9cbd262..bbd5b9dbb 100644 --- a/Metro/Metro_RP2350_Match3/match3_game/match3_game_helpers.py +++ b/Metro/Metro_RP2350_Match3/match3_game/match3_game_helpers.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MIT import os import random +import sys import time from io import BytesIO @@ -133,7 +134,10 @@ class Match3Card(Group): def __init__(self, card_tuple, **kwargs): # tile palette mapper to color the card - self._mapper = TilePaletteMapper(kwargs["pixel_shader"], 5, 1, 1) + if sys.implementation.version[0] == 9: + self._mapper = TilePaletteMapper(kwargs["pixel_shader"], 5, 1, 1) + elif sys.implementation.version[0] >= 10: + self._mapper = TilePaletteMapper(kwargs["pixel_shader"], 5) kwargs["pixel_shader"] = self._mapper # tile grid to for the visible sprite self._tilegrid = TileGrid(**kwargs) @@ -580,9 +584,11 @@ class Match3Game(Group): # if 3 cards have been clicked if len(self.clicked_cards) == 3: # check if the 3 cards make a valid set - valid_set = validate_set(self.clicked_cards[0], - self.clicked_cards[1], - self.clicked_cards[2]) + valid_set = validate_set( + self.clicked_cards[0], + self.clicked_cards[1], + self.clicked_cards[2], + ) # if they are a valid set if valid_set: @@ -660,7 +666,7 @@ class Match3Game(Group): # load the game from the given game state self.load_from_game_state(self.game_state) # hide the title screen - self.title_screen.hidden = True # pylint: disable=attribute-defined-outside-init + self.title_screen.hidden = True # set the current state to open play self.cur_state = STATE_PLAYING_OPEN @@ -676,7 +682,7 @@ class Match3Game(Group): # initialize a new game self.init_new_game() # hide the title screen - self.title_screen.hidden = True # pylint: disable=attribute-defined-outside-init + self.title_screen.hidden = True # set the current state to open play self.cur_state = STATE_PLAYING_OPEN @@ -727,6 +733,7 @@ class Match3TitleScreen(Group): def __init__(self, display_size): super().__init__() + self.hidden = False self.display_size = display_size # background bitmap color bg_bmp = Bitmap(display_size[0] // 10, display_size[1] // 10, 1) diff --git a/Metro/Metro_RP2350_Match3/tilepalettemapper_demo/code.py b/Metro/Metro_RP2350_Match3/tilepalettemapper_demo/code.py index 45c82cc39..d8c9571a0 100644 --- a/Metro/Metro_RP2350_Match3/tilepalettemapper_demo/code.py +++ b/Metro/Metro_RP2350_Match3/tilepalettemapper_demo/code.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2025 Tim Cocks for Adafruit Industries # # SPDX-License-Identifier: MIT +import sys import supervisor from displayio import Group, OnDiskBitmap, TileGrid @@ -20,16 +21,28 @@ display.root_group = main_group spritesheet_bmp = OnDiskBitmap("match3_cards_spritesheet.bmp") # create a TilePaletteMapper -tile_palette_mapper = TilePaletteMapper( - spritesheet_bmp.pixel_shader, # input pixel_shader - 5, # input color count - 3, # grid width - 1 # grid height -) +if sys.implementation.version[0] == 9: + tile_palette_mapper = TilePaletteMapper( + spritesheet_bmp.pixel_shader, # input pixel_shader + 5, # input color count + 3, # grid width + 1, # grid height + ) +elif sys.implementation.version[0] >= 10: + tile_palette_mapper = TilePaletteMapper( + spritesheet_bmp.pixel_shader, # input pixel_shader + 5, # input color count + ) # create a TileGrid to show some cards -cards_tilegrid = TileGrid(spritesheet_bmp, pixel_shader=tile_palette_mapper, - width=3, height=1, tile_width=24, tile_height=32) +cards_tilegrid = TileGrid( + spritesheet_bmp, + pixel_shader=tile_palette_mapper, + width=3, + height=1, + tile_width=24, + tile_height=32, +) # set each tile in the grid to a different sprite index cards_tilegrid[0, 0] = 10 diff --git a/Metro/Metro_RP2350_Matching_Game/bitmaps/foreground.bmp b/Metro/Metro_RP2350_Matching_Game/bitmaps/foreground.bmp new file mode 100755 index 000000000..a546be7af Binary files /dev/null and b/Metro/Metro_RP2350_Matching_Game/bitmaps/foreground.bmp differ diff --git a/Metro/Metro_RP2350_Matching_Game/bitmaps/game_sprites.bmp b/Metro/Metro_RP2350_Matching_Game/bitmaps/game_sprites.bmp new file mode 100755 index 000000000..89ac3effb Binary files /dev/null and b/Metro/Metro_RP2350_Matching_Game/bitmaps/game_sprites.bmp differ diff --git a/Metro/Metro_RP2350_Matching_Game/bitmaps/mouse_cursor.bmp b/Metro/Metro_RP2350_Matching_Game/bitmaps/mouse_cursor.bmp new file mode 100755 index 000000000..f941ad29b Binary files /dev/null and b/Metro/Metro_RP2350_Matching_Game/bitmaps/mouse_cursor.bmp differ diff --git a/Metro/Metro_RP2350_Matching_Game/code.py b/Metro/Metro_RP2350_Matching_Game/code.py new file mode 100755 index 000000000..f6c903489 --- /dev/null +++ b/Metro/Metro_RP2350_Matching_Game/code.py @@ -0,0 +1,264 @@ +# SPDX-FileCopyrightText: 2025 Melissa LeBlanc-Williams for Adafruit Industries +# SPDX-License-Identifier: MIT +""" +An implementation of a match3 jewel swap game. The idea is to move one character at a time +to line up at least 3 characters. +""" +import time +from displayio import Group, OnDiskBitmap, TileGrid, Bitmap, Palette +from adafruit_display_text.bitmap_label import Label +from adafruit_display_text.text_box import TextBox +from eventbutton import EventButton +import supervisor +import terminalio +from adafruit_usb_host_mouse import find_and_init_boot_mouse +from gamelogic import GameLogic, SELECTOR_SPRITE, EMPTY_SPRITE, GAMEBOARD_POSITION + +GAMEBOARD_SIZE = (8, 7) +HINT_TIMEOUT = 10 # seconds before hint is shown +GAME_PIECES = 7 # Number of different game pieces (set between 3 and 8) + +# pylint: disable=ungrouped-imports +if hasattr(supervisor.runtime, "display") and supervisor.runtime.display is not None: + # use the built-in HSTX display for Metro RP2350 + display = supervisor.runtime.display +else: + # pylint: disable=ungrouped-imports + from displayio import release_displays + import picodvi + import board + import framebufferio + + # initialize display + release_displays() + + fb = picodvi.Framebuffer( + 320, + 240, + clk_dp=board.CKP, + clk_dn=board.CKN, + red_dp=board.D0P, + red_dn=board.D0N, + green_dp=board.D1P, + green_dn=board.D1N, + blue_dp=board.D2P, + blue_dn=board.D2N, + color_depth=16, + ) + display = framebufferio.FramebufferDisplay(fb) + +def get_color_index(color, shader=None): + for index, palette_color in enumerate(shader): + if palette_color == color: + return index + return None + +# Load the spritesheet +sprite_sheet = OnDiskBitmap("/bitmaps/game_sprites.bmp") +sprite_sheet.pixel_shader.make_transparent( + get_color_index(0x00ff00, sprite_sheet.pixel_shader) +) + +# Main group will hold all the visual layers +main_group = Group() +display.root_group = main_group + +# Add Background to the Main Group +background = Bitmap(display.width, display.height, 1) +bg_color = Palette(1) +bg_color[0] = 0x333333 +main_group.append(TileGrid( + background, + pixel_shader=bg_color +)) + +# Add Game grid, which holds the game board, to the main group +game_grid = TileGrid( + sprite_sheet, + pixel_shader=sprite_sheet.pixel_shader, + width=GAMEBOARD_SIZE[0], + height=GAMEBOARD_SIZE[1], + tile_width=32, + tile_height=32, + x=GAMEBOARD_POSITION[0], + y=GAMEBOARD_POSITION[1], + default_tile=EMPTY_SPRITE, +) +main_group.append(game_grid) + +# Add a special selection groupd to highlight the selected piece and allow animation +selected_piece_group = Group() +selected_piece = TileGrid( + sprite_sheet, + pixel_shader=sprite_sheet.pixel_shader, + width=1, + height=1, + tile_width=32, + tile_height=32, + x=0, + y=0, + default_tile=EMPTY_SPRITE, +) +selected_piece_group.append(selected_piece) +selector = TileGrid( + sprite_sheet, + pixel_shader=sprite_sheet.pixel_shader, + width=1, + height=1, + tile_width=32, + tile_height=32, + x=0, + y=0, + default_tile=SELECTOR_SPRITE, +) +selected_piece_group.append(selector) +selected_piece_group.hidden = True +main_group.append(selected_piece_group) + +# Add a group for the swap piece to help with animation +swap_piece = TileGrid( + sprite_sheet, + pixel_shader=sprite_sheet.pixel_shader, + width=1, + height=1, + tile_width=32, + tile_height=32, + x=0, + y=0, + default_tile=EMPTY_SPRITE, +) +swap_piece.hidden = True +main_group.append(swap_piece) + +# Add foreground +foreground_bmp = OnDiskBitmap("/bitmaps/foreground.bmp") +foreground_bmp.pixel_shader.make_transparent(0) +foreground_tg = TileGrid(foreground_bmp, pixel_shader=foreground_bmp.pixel_shader) +foreground_tg.x = 0 +foreground_tg.y = 0 +main_group.append(foreground_tg) + +# Add a group for the UI Elements +ui_group = Group() +main_group.append(ui_group) + +# Create the mouse graphics and add to the main group +time.sleep(1) # Allow time for USB host to initialize +mouse = find_and_init_boot_mouse("/bitmaps/mouse_cursor.bmp") +if mouse is None: + raise RuntimeError("No mouse found connected to USB Host") +main_group.append(mouse.tilegrid) + +# Create the game logic object +# pylint: disable=no-value-for-parameter, too-many-function-args +game_logic = GameLogic( + display, + mouse, + game_grid, + swap_piece, + selected_piece_group, + GAME_PIECES, + HINT_TIMEOUT +) + +def update_ui(): + # Update the UI elements with the current game state + score_label.text = f"Score:\n{game_logic.score}" + +waiting_for_release = False +game_over_shown = False + +# Create the UI Elements +# Label for the Score +score_label = Label( + terminalio.FONT, + color=0xffff00, + x=5, + y=10, +) +ui_group.append(score_label) + +message_dialog = Group() +message_dialog.hidden = True + +def reset(): + global game_over_shown # pylint: disable=global-statement + # Reset the game logic + game_logic.reset() + message_dialog.hidden = True + game_over_shown = False + +def hide_group(group): + group.hidden = True + +reset() + +reset_button = EventButton( + reset, + label="Reset", + width=40, + height=16, + x=5, + y=50, + style=EventButton.RECT, +) +ui_group.append(reset_button) + +message_label = TextBox( + terminalio.FONT, + text="", + color=0x333333, + background_color=0xEEEEEE, + width=display.width // 3, + height=90, + align=TextBox.ALIGN_CENTER, + padding_top=5, +) +message_label.anchor_point = (0, 0) +message_label.anchored_position = ( + display.width // 2 - message_label.width // 2, + display.height // 2 - message_label.height // 2, +) +message_dialog.append(message_label) +message_button = EventButton( + (hide_group, message_dialog), + label="OK", + width=40, + height=16, + x=display.width // 2 - 20, + y=display.height // 2 - message_label.height // 2 + 60, + style=EventButton.RECT, +) +message_dialog.append(message_button) +ui_group.append(message_dialog) + +# main loop +while True: + update_ui() + # update mouse + game_logic.update_mouse() + + if not message_dialog.hidden: + if message_button.handle_mouse( + (mouse.x, mouse.y), + game_logic.pressed_btns and "left" in game_logic.pressed_btns, + waiting_for_release + ): + game_logic.waiting_for_release = True + continue + + if reset_button.handle_mouse( + (mouse.x, mouse.y), + game_logic.pressed_btns is not None and "left" in game_logic.pressed_btns, + game_logic.waiting_for_release + ): + game_logic.waiting_for_release = True + + # process gameboard click if no menu + game_logic.update() + game_over = game_logic.check_for_game_over() + if game_over and not game_over_shown: + message_label.text = ("No more moves available. your final score is:\n" + + str(game_logic.score)) + message_dialog.hidden = False + game_over_shown = True diff --git a/Metro/Metro_RP2350_Matching_Game/eventbutton.py b/Metro/Metro_RP2350_Matching_Game/eventbutton.py new file mode 100755 index 000000000..766cd9605 --- /dev/null +++ b/Metro/Metro_RP2350_Matching_Game/eventbutton.py @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2025 Melissa LeBlanc-Williams for Adafruit Industries +# SPDX-License-Identifier: MIT + +from adafruit_button import Button + +class EventButton(Button): + """A button that can be used to trigger a callback when clicked. + + :param callback: The callback function to call when the button is clicked. + A tuple can be passed with an argument that will be passed to the + callback function. The first element of the tuple should be the + callback function, and the remaining elements will be passed as + arguments to the callback function. + """ + def __init__(self, callback, *args, **kwargs): + super().__init__(*args, **kwargs) + self.args = [] + self.selected = False + if isinstance(callback, tuple): + self.callback = callback[0] + self.args = callback[1:] + else: + self.callback = callback + + def click(self): + """Call the function when the button is pressed.""" + self.callback(*self.args) + + def handle_mouse(self, point, clicked, waiting_for_release): + if waiting_for_release: + return False + + # Handle mouse events for the button + if self.contains(point): + self.selected = True + if clicked: + self.click() + return True + else: + self.selected = False + return False diff --git a/Metro/Metro_RP2350_Matching_Game/gamelogic.py b/Metro/Metro_RP2350_Matching_Game/gamelogic.py new file mode 100755 index 000000000..d89f47ea9 --- /dev/null +++ b/Metro/Metro_RP2350_Matching_Game/gamelogic.py @@ -0,0 +1,525 @@ +# SPDX-FileCopyrightText: 2025 Melissa LeBlanc-Williams for Adafruit Industries +# SPDX-License-Identifier: MIT + +import random +import time +from adafruit_ticks import ticks_ms + +GAMEBOARD_POSITION = (55, 8) + +SELECTOR_SPRITE = 9 +EMPTY_SPRITE = 10 +DEBOUNCE_TIME = 0.2 # seconds for debouncing mouse clicks + +class GameBoard: + "Contains the game board" + def __init__(self, game_grid, swap_piece, selected_piece_group): + self.x = GAMEBOARD_POSITION[0] + self.y = GAMEBOARD_POSITION[1] + self._game_grid = game_grid + self._selected_coords = None + self._selected_piece = selected_piece_group[0] + self._selector = selected_piece_group[1] + self._swap_piece = swap_piece + self.selected_piece_group = selected_piece_group + + def add_game_piece(self, column, row, piece_type): + if 0 <= column < self.columns and 0 <= row < self.rows: + if self._game_grid[(column, row)] != EMPTY_SPRITE: + raise ValueError("Position already occupied") + self._game_grid[(column, row)] = piece_type + else: + raise IndexError("Position out of bounds") + + def remove_game_piece(self, column, row): + if 0 <= column < self.columns and 0 <= row < self.rows: + self._game_grid[(column, row)] = EMPTY_SPRITE + else: + raise IndexError("Position out of bounds") + + def reset(self): + for column in range(self.columns): + for row in range(self.rows): + if self._game_grid[(column, row)] != EMPTY_SPRITE: + self.remove_game_piece(column, row) + # Hide the animation TileGrids + self._selector.hidden = True + self._swap_piece.hidden = True + self.selected_piece_group.hidden = True + + def move_game_piece(self, old_x, old_y, new_x, new_y): + if 0 <= old_x < self.columns and 0 <= old_y < self.rows: + if 0 <= new_x < self.columns and 0 <= new_y < self.rows: + if self._game_grid[(new_x, new_y)] == EMPTY_SPRITE: + self._game_grid[(new_x, new_y)] = self._game_grid[(old_x, old_y)] + self._game_grid[(old_x, old_y)] = EMPTY_SPRITE + else: + raise ValueError("New position already occupied") + else: + raise IndexError("New position out of bounds") + else: + raise IndexError("Old position out of bounds") + + @property + def columns(self): + return self._game_grid.width + + @property + def rows(self): + return self._game_grid.height + + @property + def selected_piece(self): + if self._selected_coords is not None and self._selected_piece[0] != EMPTY_SPRITE: + return self._selected_piece[0] + return None + + @property + def swap_piece(self): + return self._swap_piece + + def set_swap_piece(self, column, row): + # Set the swap piece to the piece at the specified coordinates + piece = self.get_piece(column, row) + if self._swap_piece[0] is None and self._swap_piece[0] == EMPTY_SPRITE: + raise ValueError("Can't swap an empty piece") + if self._swap_piece.hidden: + self._swap_piece[0] = piece + self._swap_piece.x = column * 32 + self.x + self._swap_piece.y = row * 32 + self.y + self._swap_piece.hidden = False + self._game_grid[(column, row)] = EMPTY_SPRITE + else: + self._game_grid[(column, row)] = self._swap_piece[0] + self._swap_piece[0] = EMPTY_SPRITE + self._swap_piece.hidden = True + + @property + def selected_coords(self): + if self._selected_coords is not None: + return self._selected_coords + return None + + @property + def selector_hidden(self): + return self._selector.hidden + + @selector_hidden.setter + def selector_hidden(self, value): + # Set the visibility of the selector + self._selector.hidden = value + + def set_selected_coords(self, column, row): + # Set the selected coordinates to the specified column and row + if 0 <= column < self.columns and 0 <= row < self.rows: + self._selected_coords = (column, row) + self.selected_piece_group.x = column * 32 + self.x + self.selected_piece_group.y = row * 32 + self.y + else: + raise IndexError("Selected coordinates out of bounds") + + def select_piece(self, column, row, show_selector=True): + # Take care of selecting a piece + piece = self.get_piece(column, row) + if self.selected_piece is None and piece == EMPTY_SPRITE: + # If no piece is selected and the clicked piece is empty, do nothing + return + + if (self.selected_piece is not None and + (self._selected_coords[0] != column or self._selected_coords[1] != row)): + # If a piece is already selected and the coordinates don't match, do nothing + return + + if self.selected_piece is None: + # No piece selected, so select the specified piece + self._selected_piece[0] = self.get_piece(column, row) + self._selected_coords = (column, row) + self.selected_piece_group.x = column * 32 + self.x + self.selected_piece_group.y = row * 32 + self.y + self.selected_piece_group.hidden = False + self.selector_hidden = not show_selector + self._game_grid[(column, row)] = EMPTY_SPRITE + else: + self._game_grid[(column, row)] = self._selected_piece[0] + self._selected_piece[0] = EMPTY_SPRITE + self.selected_piece_group.hidden = True + self._selected_coords = None + + def get_piece(self, column, row): + if 0 <= column < self.columns and 0 <= row < self.rows: + return self._game_grid[(column, row)] + return None + + @property + def game_grid_copy(self): + # Return a copy of the game grid as a 2D list + return [[self._game_grid[(x, y)] for x in range(self.columns)] for y in range(self.rows)] + +class GameLogic: + "Contains the Logic to examine the game board and determine if a move is valid." + def __init__(self, display, mouse, game_grid, swap_piece, + selected_piece_group, game_pieces, hint_timeout): + self._display = display + self._mouse = mouse + self.game_board = GameBoard(game_grid, swap_piece, selected_piece_group) + self._score = 0 + self._available_moves = [] + if not 3 <= game_pieces <= 8: + raise ValueError("game_pieces must be between 3 and 8") + self._game_pieces = game_pieces # Number of different game pieces + self._hint_timeout = hint_timeout + self._last_update_time = ticks_ms() # For hint timing + self._last_click_time = ticks_ms() # For debouncing mouse clicks + self.pressed_btns = None + self.waiting_for_release = False + + def update_mouse(self): + self.pressed_btns = self._mouse.update() + if self.waiting_for_release and not self.pressed_btns: + # If both buttons are released, we can process the next click + self.waiting_for_release = False + + def update(self): + gb = self.game_board + if (gb.x <= self._mouse.x <= gb.x + gb.columns * 32 and + gb.y <= self._mouse.y <= gb.y + gb.rows * 32 and + not self.waiting_for_release): + piece_coords = ((self._mouse.x - gb.x) // 32, (self._mouse.y - gb.y) // 32) + if self.pressed_btns and "left" in self.pressed_btns: + self._piece_clicked(piece_coords) + self.waiting_for_release = True + if self.time_since_last_update > self._hint_timeout: + self.show_hint() + + def _piece_clicked(self, coords): + """ Handle a piece click event. """ + if ticks_ms() <= self._last_click_time: + self._last_click_time -= 2**29 # ticks_ms() wraps around after 2**29 ms + + if ticks_ms() <= self._last_click_time + (DEBOUNCE_TIME * 1000): + print("Debouncing click, too soon after last click.") + return + self._last_click_time = ticks_ms() # Update last click time + + column, row = coords + self._last_update_time = ticks_ms() + # Check if the clicked piece is valid + if not 0 <= column < self.game_board.columns or not 0 <= row < self.game_board.rows: + print(f"Clicked coordinates ({column}, {row}) are out of bounds.") + return + + # If clicked piece is empty and no piece is selected, do nothing + if (self.game_board.get_piece(column, row) == EMPTY_SPRITE and + self.game_board.selected_piece is None): + print(f"No piece at ({column}, {row}) and no piece selected.") + return + + if self.game_board.selected_piece is None: + # If no piece is selected, select the piece at the clicked coordinates + self.game_board.select_piece(column, row) + return + + if (self.game_board.selected_coords is not None and + (self.game_board.selected_coords[0] == column and + self.game_board.selected_coords[1] == row)): + # If the clicked piece is already selected, deselect it + self.game_board.select_piece(column, row) + return + + # If piece is selected and the new coordinates are 1 position + # away horizontally or vertically, swap the pieces + if self.game_board.selected_coords is not None: + previous_x, previous_y = self.game_board.selected_coords + if ((abs(previous_x - column) == 1 and previous_y == row) or + (previous_x == column and abs(previous_y - row) == 1)): + # Swap the pieces + self._swap_selected_piece(column, row) + + def show_hint(self): + """ Show a hint by selecting a random available + move and swapping the pieces back and forth. """ + if self._available_moves: + move = random.choice(self._available_moves) + from_coords = move['from'] + to_coords = move['to'] + self.game_board.select_piece(from_coords[0], from_coords[1]) + self._animate_swap(to_coords[0], to_coords[1]) + self.game_board.select_piece(from_coords[0], from_coords[1]) + self._animate_swap(to_coords[0], to_coords[1]) + self._last_update_time = ticks_ms() # Reset hint timer + + def _swap_selected_piece(self, column, row): + """ Swap the selected piece with the piece at the specified column and row. + If the swap is not valid, revert to the previous selection. """ + old_coords = self.game_board.selected_coords + self._animate_swap(column, row) + if not self._update_board(): + self.game_board.select_piece(column, row, show_selector=False) + self._animate_swap(old_coords[0], old_coords[1]) + + def _animate_swap(self, column, row): + """ Copy the pieces to separate tilegrids, animate the swap, and update the game board. """ + if 0 <= column < self.game_board.columns and 0 <= row < self.game_board.rows: + selected_coords = self.game_board.selected_coords + if selected_coords is None: + print("No piece selected to swap.") + return + + # Set the swap piece value to the column, row value + self.game_board.set_swap_piece(column, row) + self.game_board.selector_hidden = True + + # Calculate the steps for animation to move the pieces in the correct direction + selected_piece_steps = ( + (self.game_board.swap_piece.x - self.game_board.selected_piece_group.x) // 32, + (self.game_board.swap_piece.y - self.game_board.selected_piece_group.y) // 32 + ) + swap_piece_steps = ( + (self.game_board.selected_piece_group.x - self.game_board.swap_piece.x) // 32, + (self.game_board.selected_piece_group.y - self.game_board.swap_piece.y) // 32 + ) + + # Move the tilegrids in small steps to create an animation effect + for _ in range(32): + # Move the selected piece tilegrid to the swap piece position + self.game_board.selected_piece_group.x += selected_piece_steps[0] + self.game_board.selected_piece_group.y += selected_piece_steps[1] + # Move the swap piece tilegrid to the selected piece position + self.game_board.swap_piece.x += swap_piece_steps[0] + self.game_board.swap_piece.y += swap_piece_steps[1] + time.sleep(0.002) + + # Set the existing selected piece coords to the swap piece value + self.game_board.set_swap_piece(selected_coords[0], selected_coords[1]) + + # Update the selected piece coordinates to the new column, row + self.game_board.set_selected_coords(column, row) + + # Deselect the selected piece (which sets the value) + self.game_board.select_piece(column, row) + + def _apply_gravity(self): + """ Go through each column from the bottom up and move pieces down + continue until there are no more pieces to move """ + # pylint:disable=too-many-nested-blocks + while True: + self.pressed_btns = self._mouse.update() + moved = False + for x in range(self.game_board.columns): + for y in range(self.game_board.rows - 1, -1, -1): + piece = self.game_board.get_piece(x, y) + if piece != EMPTY_SPRITE: + # Check if the piece can fall + for new_y in range(y + 1, self.game_board.rows): + if self.game_board.get_piece(x, new_y) == EMPTY_SPRITE: + # Move the piece down + self.game_board.move_game_piece(x, y, x, new_y) + moved = True + break + # If the piece was in the top slot before falling, add a new piece + if y == 0 and self.game_board.get_piece(x, 0) == EMPTY_SPRITE: + self.game_board.add_game_piece(x, 0, random.randint(0, self._game_pieces)) + moved = True + if not moved: + break + + def _check_for_matches(self): + """ Scan the game board for matches of 3 or more in a row or column """ + matches = [] + for x in range(self.game_board.columns): + for y in range(self.game_board.rows): + piece = self.game_board.get_piece(x, y) + if piece != EMPTY_SPRITE: + # Check horizontal matches + horizontal_match = [(x, y)] + for dx in range(1, 3): + if (x + dx < self.game_board.columns and + self.game_board.get_piece(x + dx, y) == piece): + horizontal_match.append((x + dx, y)) + else: + break + if len(horizontal_match) >= 3: + matches.append(horizontal_match) + + # Check vertical matches + vertical_match = [(x, y)] + for dy in range(1, 3): + if (y + dy < self.game_board.rows and + self.game_board.get_piece(x, y + dy) == piece): + vertical_match.append((x, y + dy)) + else: + break + if len(vertical_match) >= 3: + matches.append(vertical_match) + return matches + + def _update_board(self): + """ Update the game logic, check for matches, and apply gravity. """ + matches_found = False + multiplier = 1 + matches = self._check_for_matches() + while matches: + if matches: + for match in matches: + for x, y in match: + self.game_board.remove_game_piece(x, y) + self._score += 10 * multiplier * len(matches) * (len(match) - 2) + time.sleep(0.5) # Pause to show the match removal + self._apply_gravity() + matches_found = True + matches = self._check_for_matches() + multiplier += 1 + self._available_moves = self._find_all_possible_matches() + print(f"{len(self._available_moves)} available moves found.") + return matches_found + + def reset(self): + """ Reset the game board and score. """ + print("Reset started") + self.game_board.reset() + self._score = 0 + self._last_update_time = ticks_ms() + self._apply_gravity() + self._update_board() + print("Reset completed") + + def _check_match_after_move(self, row, column, direction, move_type='horizontal'): + """ Move the piece in a copy of the board to see if it creates a match.""" + if move_type == 'horizontal': + new_row, new_column = row, column + direction + else: # vertical + new_row, new_column = row + direction, column + + # Check if move is within bounds + if (new_row < 0 or new_row >= self.game_board.rows or + new_column < 0 or new_column >= self.game_board.columns): + return False, False + + # Create a copy of the grid with the moved piece + new_grid = self.game_board.game_grid_copy + piece = new_grid[row][column] + new_grid[row][column], new_grid[new_row][new_column] = new_grid[new_row][new_column], piece + + # Check for horizontal matches at the new position + horizontal_match = self._check_horizontal_match(new_grid, new_row, new_column, piece) + + # Check for vertical matches at the new position + vertical_match = self._check_vertical_match(new_grid, new_row, new_column, piece) + + # Also check the original position for matches after the swap + original_piece = new_grid[row][column] + horizontal_match_orig = self._check_horizontal_match(new_grid, row, column, original_piece) + vertical_match_orig = self._check_vertical_match(new_grid, row, column, original_piece) + + all_matches = (horizontal_match + vertical_match + + horizontal_match_orig + vertical_match_orig) + + return True, len(all_matches) > 0 + + @staticmethod + def _check_horizontal_match(grid, row, column, piece): + """Check for horizontal 3-in-a-row matches centered + around or including the given position.""" + matches = [] + columns = len(grid[0]) + + # Check all possible 3-piece horizontal combinations that include this position + for start_column in range(max(0, column - 2), min(columns - 2, column + 1)): + if (start_column + 2 < columns and + grid[row][start_column] == piece and + grid[row][start_column + 1] == piece and + grid[row][start_column + 2] == piece): + matches.append([(row, start_column), + (row, start_column + 1), + (row, start_column + 2)]) + + return matches + + @staticmethod + def _check_vertical_match(grid, row, column, piece): + """Check for vertical 3-in-a-row matches centered around or including the given position.""" + matches = [] + rows = len(grid) + + # Check all possible 3-piece vertical combinations that include this position + for start_row in range(max(0, row - 2), min(rows - 2, row + 1)): + if (start_row + 2 < rows and + grid[start_row][column] == piece and + grid[start_row + 1][column] == piece and + grid[start_row + 2][column] == piece): + matches.append([(start_row, column), + (start_row + 1, column), + (start_row + 2, column)]) + + return matches + + def check_for_game_over(self): + """ Check if there are no available moves left on the game board. """ + if not self._available_moves: + return True + return False + + def _find_all_possible_matches(self): + """ + Scan the entire game board to find all possible moves that would create a 3-in-a-row match. + """ + possible_moves = [] + + for row in range(self.game_board.rows): + for column in range(self.game_board.columns): + # Check move right + can_move, creates_match = self._check_match_after_move( + row, column, 1, 'horizontal') + if can_move and creates_match: + possible_moves.append({ + 'from': (column, row), + 'to': (column + 1, row), + }) + + # Check move left + can_move, creates_match = self._check_match_after_move( + row, column, -1, 'horizontal') + if can_move and creates_match: + possible_moves.append({ + 'from': (column, row), + 'to': (column - 1, row), + }) + + # Check move down + can_move, creates_match = self._check_match_after_move( + row, column, 1, 'vertical') + if can_move and creates_match: + possible_moves.append({ + 'from': (column, row), + 'to': (column, row + 1), + }) + + # Check move up + can_move, creates_match = self._check_match_after_move( + row, column, -1, 'vertical') + if can_move and creates_match: + possible_moves.append({ + 'from': (column, row), + 'to': (column, row - 1), + }) + + # Remove duplicates because from and to can be reversed + unique_moves = set() + for move in possible_moves: + from_coords = tuple(move['from']) + to_coords = tuple(move['to']) + if from_coords > to_coords: + unique_moves.add((to_coords, from_coords)) + else: + unique_moves.add((from_coords, to_coords)) + possible_moves = [{'from': move[0], 'to': move[1]} for move in unique_moves] + + return possible_moves + + @property + def score(self): + return self._score + + @property + def time_since_last_update(self): + return (ticks_ms() - self._last_update_time) / 1000.0 diff --git a/Metro/Metro_RP2350_Minesweeper/code.py b/Metro/Metro_RP2350_Minesweeper/code.py index 3f1a19f36..6e4aeded2 100755 --- a/Metro/Metro_RP2350_Minesweeper/code.py +++ b/Metro/Metro_RP2350_Minesweeper/code.py @@ -197,13 +197,15 @@ def reset(): def set_difficulty(diff): game_logic.difficulty = diff reset() + difficulty_menu.select_item(DIFFICULTIES[diff]['label'].lower().replace(" ", "_")) def hide_group(group): group.hidden = True for i, difficulty in enumerate(DIFFICULTIES): # Create a button for each difficulty - difficulty_menu.add_item((set_difficulty, i), difficulty['label']) + selected = i == game_logic.difficulty + difficulty_menu.add_item((set_difficulty, i), difficulty['label'], selected) reset_menu.add_item(reset, "OK") diff --git a/Metro/Metro_RP2350_Minesweeper/gamelogic.py b/Metro/Metro_RP2350_Minesweeper/gamelogic.py index 61a90b494..a66157898 100755 --- a/Metro/Metro_RP2350_Minesweeper/gamelogic.py +++ b/Metro/Metro_RP2350_Minesweeper/gamelogic.py @@ -68,8 +68,12 @@ class GameLogic: if (self.grid_width * 16 > self._display.width or self.grid_height * 16 > self._display.height - INFO_BAR_HEIGHT): raise ValueError("Grid size exceeds display size") - self._board_data = bytearray(self.grid_width * self.grid_height) self._mine_count = DIFFICULTIES[self._difficulty]['mines'] + if self._mine_count > (self.grid_width - 1) * (self.grid_height - 1): + raise ValueError("Too many mines for grid size") + if self._mine_count < 10: + raise ValueError("There must be at least 10 mines") + self._board_data = bytearray(self.grid_width * self.grid_height) self._status = STATUS_NEWGAME self._start_time = None self._end_time = None @@ -241,7 +245,6 @@ class GameLogic: if self._start_time is None: return 0 if self._end_time is None: - print(ticks_ms() / 1000, self._start_time / 1000) return min(999, (ticks_ms() - self._start_time) // 1000) return min(999, (self._end_time - self._start_time) // 1000) diff --git a/Metro/Metro_RP2350_Minesweeper/menu.py b/Metro/Metro_RP2350_Minesweeper/menu.py index 4b397de72..1b1dc3109 100755 --- a/Metro/Metro_RP2350_Minesweeper/menu.py +++ b/Metro/Metro_RP2350_Minesweeper/menu.py @@ -36,17 +36,28 @@ class SubMenu(Group): self._menu_items = [] self._root_button = None - def add_item(self, function, label): + def add_item(self, function, label, selected=False): + key = label.lower().replace(" ", "_") self._menu_items.append( { + "key": key, "function": function, "label": label, + "selected": selected, } ) self._render() + def select_item(self, key): + for item in self._menu_items: + if item["key"] == key: + item["selected"] = True + else: + item["selected"] = False + self._render() + @staticmethod - def _create_button(callback, label, width, x, y=0, border=True): + def _create_button(callback, label, width, x, y=0, border=True, selected=False): if border: outline_color = 0x000000 selected_outline = 0x333333 @@ -54,6 +65,11 @@ class SubMenu(Group): outline_color = 0xEEEEEE selected_outline = 0xBBBBBB + if selected: + selected_label = label_color = 0x008800 + else: + selected_label = label_color = 0x333333 + button = EventButton( callback, x=x, @@ -64,13 +80,14 @@ class SubMenu(Group): style=EventButton.RECT, fill_color=0xEEEEEE, outline_color=outline_color, - label_color=0x333333, + label_color=label_color, selected_fill=0xBBBBBB, - selected_label=0x333333, + selected_label=selected_label, selected_outline=selected_outline, ) return button + def _toggle_submenu(self): self._menu_items_group.hidden = not self._menu_items_group.hidden @@ -87,7 +104,7 @@ class SubMenu(Group): self._button_width, self._xpos, self._ypos, - border=True, + True, ) self.append(self._root_button) @@ -113,7 +130,8 @@ class SubMenu(Group): self._menu_width - 2, self._xpos + 1, self._ypos + index * MENU_ITEM_HEIGHT + self._root_button.height, - border=False, + False, + item["selected"], ) self._menu_items_group.append(button) diff --git a/Metro_RP2350_Examples/CircuitPython_SDCard_ListFiles/code.py b/Metro_RP2350_Examples/CircuitPython_SDCard_ListFiles/code.py index be5386756..392426831 100644 --- a/Metro_RP2350_Examples/CircuitPython_SDCard_ListFiles/code.py +++ b/Metro_RP2350_Examples/CircuitPython_SDCard_ListFiles/code.py @@ -3,7 +3,6 @@ """ CircuitPython Essentials SD Card Read Demo - """ import os @@ -16,14 +15,28 @@ import adafruit_sdcard # The SD_CS pin is the chip select line. SD_CS = board.SD_CS -# Connect to the card and mount the filesystem. -cs = digitalio.DigitalInOut(SD_CS) -sdcard = adafruit_sdcard.SDCard(busio.SPI(board.SD_SCK, board.SD_MOSI, board.SD_MISO), cs) -vfs = storage.VfsFat(sdcard) -storage.mount(vfs, "/sd") +# For CircuitPython 9.x we must initialize and mount the SD card manually. +# On CircuitPython 10.x the core will automatically initialize and mount the SD. +# This try/except block can be removed with 10.x has a stable release. +try: + # Initialize the Chip Select pin for the SD card + cs = digitalio.DigitalInOut(SD_CS) + # Initialize the SD card + sdcard = adafruit_sdcard.SDCard( + busio.SPI(board.SD_SCK, board.SD_MOSI, board.SD_MISO), cs + ) + # Mount the SD card + vfs = storage.VfsFat(sdcard) + storage.mount(vfs, "/sd") +except ValueError: + # "ValueError SD_CS in use" error happen on CircuitPython 10.x + # because the core initialized the SD automatically. The error + # can be ignored on 10.x. + pass # Use the filesystem as normal! Our files are under /sd + # This helper function will print the contents of the SD def print_directory(path, tabs=0): for file in os.listdir(path): diff --git a/Metro_RP2350_Examples/CircuitPython_SDCard_Write/code.py b/Metro_RP2350_Examples/CircuitPython_SDCard_Write/code.py index 596c2b75f..5100c8aa0 100644 --- a/Metro_RP2350_Examples/CircuitPython_SDCard_Write/code.py +++ b/Metro_RP2350_Examples/CircuitPython_SDCard_Write/code.py @@ -4,10 +4,6 @@ """ CircuitPython Essentials SD Card Write Demo -REMOVE THIS LINE AND ALL BELOW IT BEFORE SUBMITTING TO LEARN -Update CHIP_SELECT_PIN to match the CS pin on your board. - -For example, for the Metro ESP32-S3, you would use: board.SD_CS. """ import time @@ -21,11 +17,25 @@ import storage # The SD_CS pin is the chip select line. SD_CS = board.SD_CS -# Connect to the card and mount the filesystem. -cs = digitalio.DigitalInOut(SD_CS) -sdcard = adafruit_sdcard.SDCard(busio.SPI(board.SD_SCK, board.SD_MOSI, board.SD_MISO), cs) -vfs = storage.VfsFat(sdcard) -storage.mount(vfs, "/sd") + +# For CircuitPython 9.x we must initialize and mount the SD card manually. +# On CircuitPython 10.x the core will automatically initialize and mount the SD. +# This try/except block can be removed with 10.x has a stable release. +try: + # Initialize the Chip Select pin for the SD card + cs = digitalio.DigitalInOut(SD_CS) + # Initialize the SD card + sdcard = adafruit_sdcard.SDCard( + busio.SPI(board.SD_SCK, board.SD_MOSI, board.SD_MISO), cs + ) + # Mount the SD card + vfs = storage.VfsFat(sdcard) + storage.mount(vfs, "/sd") +except ValueError: + # "ValueError SD_CS in use" error happen on CircuitPython 10.x + # because the core initialized the SD automatically. The error + # can be ignored on 10.x. + pass # Use the filesystem as normal! Our files are under /sd diff --git a/Mother_Of_All_Demos_Keyset/code.py b/Mother_Of_All_Demos_Keyset/code.py new file mode 100644 index 000000000..35a596e36 --- /dev/null +++ b/Mother_Of_All_Demos_Keyset/code.py @@ -0,0 +1,151 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import time +import board +import keypad +import supervisor +import usb_hid +from adafruit_hid.keyboard import Keyboard +from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS + +# Dictionary of macros for single keys and combinations +macros = { + # Single key macros + (0,): "good", + (1,): "great", + (2,): "nice", + (3,): "awesome", + (4,): "cool", + + # Combination macros + (0, 2, 4): "looks good to me", + (0, 2): "be right back", + (2, 4): "see you soon", + (1, 3): "sounds good", +} + +KEY_PINS = ( + board.A1, + board.A2, + board.A3, + board.MISO, + board.MOSI, +) + +keys = keypad.Keys( + KEY_PINS, + value_when_pressed=False, + pull=True, + interval=0.01, + max_events=64, + debounce_threshold=3 +) + +keyboard = Keyboard(usb_hid.devices) +keyboard_layout = KeyboardLayoutUS(keyboard) + +# need to wait longer for 3 key combos +LARGER_COMBOS = { + (0, 2): (0, 2, 4), + (2, 4): (0, 2, 4), +} + +# How long to wait for possible additional keys in a combo (ms) +COMBO_WAIT_TIME = 150 # Wait 150ms to see if more keys are coming + +# How long to wait for a single key before executing (ms) +SINGLE_KEY_TIMEOUT_MS = 80 + +# Minimum time between macro executions (ms) +MACRO_COOLDOWN_MS = 300 + +# Store the current state of all keys +key_states = {i: False for i in range(len(KEY_PINS))} + +# Create a reusable Event object to avoid memory allocations +reusable_event = keypad.Event() + +# Track timing and state +last_macro_time = 0 +key_combo_start_time = 0 +waiting_for_combo = False +last_executed_combo = None + +while True: + # Process all events in the queue + keys_changed = False + + while keys.events: + if keys.events.get_into(reusable_event): + # Check if key state actually changed + old_state = key_states[reusable_event.key_number] + key_states[reusable_event.key_number] = reusable_event.pressed + + if old_state != reusable_event.pressed: + print(f"Key {reusable_event.key_number} " + + f"{'pressed' if reusable_event.pressed else 'released'}") + keys_changed = True + + # Get currently pressed keys as a sorted tuple + current_pressed_keys = tuple(sorted(k for k, v in key_states.items() if v)) + current_time = supervisor.ticks_ms() + + # When all keys are released, reset tracking + if not current_pressed_keys: + waiting_for_combo = False + last_executed_combo = None + time.sleep(0.01) + continue + + # If this is a new key pattern or we just started + if keys_changed: + # If we weren't tracking before, start now + if not waiting_for_combo: + key_combo_start_time = current_time + waiting_for_combo = True + + # If the pressed keys have changed, update the timer + if current_pressed_keys != last_executed_combo: + key_combo_start_time = current_time + + # Skip if we've already executed this exact combination + if current_pressed_keys == last_executed_combo: + time.sleep(0.01) + continue + + # Determine if we should execute a macro now + should_execute = False + wait_more = False + + # If this is a potential part of a larger combo, wait longer + if current_pressed_keys in LARGER_COMBOS: + # Only wait if we've been waiting less than the combo wait time + if (current_time - key_combo_start_time) < COMBO_WAIT_TIME: + wait_more = True + else: + # We've waited long enough, go ahead and execute + should_execute = True + # Immediate execution for multi-key combinations that aren't potential parts of larger combos + elif len(current_pressed_keys) > 1: + should_execute = True + # Execute single key after timeout + elif waiting_for_combo and (current_time - key_combo_start_time) >= SINGLE_KEY_TIMEOUT_MS: + should_execute = True + + # If we need to wait more, skip to the next iteration + if wait_more: + time.sleep(0.01) + continue + + # Execute the macro if conditions are met + if should_execute and current_pressed_keys in macros: + # Only execute if cooldown period has passed + if current_time - last_macro_time >= MACRO_COOLDOWN_MS: + print(f"MACRO: {macros[current_pressed_keys]}") + keyboard_layout.write(macros[current_pressed_keys]) + last_macro_time = current_time + last_executed_combo = current_pressed_keys + + time.sleep(0.01) diff --git a/Not_A_Typewriter/Desktop_Not_A_Typewriter/CircuitPython_Serial_Typewriter/boot.py b/Not_A_Typewriter/Desktop_Not_A_Typewriter/CircuitPython_Serial_Typewriter/boot.py new file mode 100644 index 000000000..ff9f9e96e --- /dev/null +++ b/Not_A_Typewriter/Desktop_Not_A_Typewriter/CircuitPython_Serial_Typewriter/boot.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import usb_cdc + +# Enable USB CDC (serial) communication +usb_cdc.enable(console=True, data=True) diff --git a/Not_A_Typewriter/Desktop_Not_A_Typewriter/CircuitPython_Serial_Typewriter/code.py b/Not_A_Typewriter/Desktop_Not_A_Typewriter/CircuitPython_Serial_Typewriter/code.py new file mode 100644 index 000000000..06f3bb68d --- /dev/null +++ b/Not_A_Typewriter/Desktop_Not_A_Typewriter/CircuitPython_Serial_Typewriter/code.py @@ -0,0 +1,190 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +USB Typewriter Feather-side Script +Converts incoming keystrokes to solenoid clicks +""" + +import time +import struct +import usb_cdc +import board +from adafruit_mcp230xx.mcp23017 import MCP23017 + +# Typewriter configuration +KEYSTROKE_BELL_INTERVAL = 25 # Ring bell every 25 keystrokes +SOLENOID_STRIKE_TIME = 0.03 # Duration in seconds for solenoid activation +ENTER_KEY_CODE = 0x28 # HID code for Enter key +ESCAPE_KEY_CODE = 0x29 # HID code for Escape key +BACKSPACE_KEY_CODE = 0x2A # HID code for Backspace key +TAB_KEY_CODE = 0x2B # HID code for Tab key + +# Key name mapping for debug output +key_names = { + 0x04: "A", 0x05: "B", 0x06: "C", 0x07: "D", + 0x08: "E", 0x09: "F", 0x0A: "G", 0x0B: "H", + 0x0C: "I", 0x0D: "J", 0x0E: "K", 0x0F: "L", + 0x10: "M", 0x11: "N", 0x12: "O", 0x13: "P", + 0x14: "Q", 0x15: "R", 0x16: "S", 0x17: "T", + 0x18: "U", 0x19: "V", 0x1A: "W", 0x1B: "X", + 0x1C: "Y", 0x1D: "Z", + 0x1E: "1", 0x1F: "2", 0x20: "3", 0x21: "4", + 0x22: "5", 0x23: "6", 0x24: "7", 0x25: "8", + 0x26: "9", 0x27: "0", + 0x28: "ENTER", 0x29: "ESC", 0x2A: "BACKSPACE", + 0x2B: "TAB", 0x2C: "SPACE", 0x2D: "MINUS", + 0x2E: "EQUAL", 0x2F: "LBRACKET", 0x30: "RBRACKET", + 0x31: "BACKSLASH", 0x33: "SEMICOLON", 0x34: "QUOTE", + 0x35: "GRAVE", 0x36: "COMMA", 0x37: "PERIOD", + 0x38: "SLASH", 0x39: "CAPS_LOCK", + 0x4F: "RIGHT", 0x50: "LEFT", 0x51: "DOWN", 0x52: "UP", +} + +# Add F1-F12 keys +for i in range(12): + key_names[0x3A + i] = f"F{i + 1}" + +# Set up I2C and MCP23017 +i2c = board.STEMMA_I2C() +mcp = MCP23017(i2c) + +# Configure solenoid pins +noid_1 = mcp.get_pin(0) # Bell solenoid +noid_2 = mcp.get_pin(1) # Key strike solenoid +noid_1.switch_to_output(value=False) +noid_2.switch_to_output(value=False) + +# Typewriter state tracking +keystroke_count = 0 +current_keys = set() # Track currently pressed keys + +# Check if USB CDC data is available +if usb_cdc.data is None: + print("ERROR: USB CDC data not enabled!") + print("Please create a boot.py file with:") + print(" import usb_cdc") + print(" usb_cdc.enable(console=True, data=True)") + print("\nThen reset the board.") + while True: + time.sleep(1) + +serial = usb_cdc.data + +def strike_key_solenoid(): + """Activate the key strike solenoid briefly""" + noid_2.value = True + time.sleep(SOLENOID_STRIKE_TIME) + noid_2.value = False + +def ring_bell_solenoid(): + """Activate the bell solenoid briefly""" + noid_1.value = True + time.sleep(SOLENOID_STRIKE_TIME) + noid_1.value = False + +def process_key_event(mod, code, p): # pylint: disable=too-many-branches + """Process a key event from the computer""" + global keystroke_count # pylint: disable=global-statement + + # Debug output + key_name = key_names.get(code, f"0x{code:02X}") + action = "pressed" if p else "released" + + # Handle modifier display + if mod > 0: + mod_str = [] + if mod & 0x01: + mod_str.append("L_CTRL") + if mod & 0x02: + mod_str.append("L_SHIFT") + if mod & 0x04: + mod_str.append("L_ALT") + if mod & 0x08: + mod_str.append("L_GUI") + if mod & 0x10: + mod_str.append("R_CTRL") + if mod & 0x20: + mod_str.append("R_SHIFT") + if mod & 0x40: + mod_str.append("R_ALT") + if mod & 0x80: + mod_str.append("R_GUI") + print(f"[{'+'.join(mod_str)}] {key_name} {action}") + else: + print(f"{key_name} {action}") + + # Only process key presses (not releases) for solenoid activation + if p and code > 0: # key_code 0 means modifier-only update + # Check if this is a new key press + if code not in current_keys: + current_keys.add(code) + + # Increment keystroke counter + keystroke_count += 1 + + # Strike the key solenoid + strike_key_solenoid() + + # Check for special keys + if code == ENTER_KEY_CODE: + ring_bell_solenoid() + keystroke_count = 0 # Reset counter for new line + elif code == ESCAPE_KEY_CODE: + ring_bell_solenoid() + keystroke_count = 0 # Reset counter + elif code == TAB_KEY_CODE: + ring_bell_solenoid() + keystroke_count = 0 # Reset counter + elif code == BACKSPACE_KEY_CODE: + keystroke_count = 0 # Reset counter but no bell + elif keystroke_count % KEYSTROKE_BELL_INTERVAL == 0: + print(f"\n*** DING! ({keystroke_count} keystrokes) ***\n") + ring_bell_solenoid() + + print(f"Total keystrokes: {keystroke_count}") + + elif not p and code > 0: + # Remove key from pressed set when released + current_keys.discard(code) + +print("USB Typewriter Receiver starting...") +print(f"Bell will ring every {KEYSTROKE_BELL_INTERVAL} keystrokes or on special keys") +print("Waiting for key events from computer...") +print("-" * 40) + +# Buffer for incoming data +buffer = bytearray(4) +buffer_pos = 0 + +while True: + # Check for incoming serial data + if serial.in_waiting > 0: + # Read available bytes + data = serial.read(serial.in_waiting) + + for byte in data: + # Look for start marker + if buffer_pos == 0: + if byte == 0xAA: + buffer[0] = byte + buffer_pos = 1 + else: + # Fill buffer + buffer[buffer_pos] = byte + buffer_pos += 1 + + # Process complete message + if buffer_pos >= 4: + # Unpack the message + _, modifier, key_code, pressed = struct.unpack('BBBB', buffer) + + # Process the key event + process_key_event(modifier, key_code, pressed) + + # Reset buffer + buffer_pos = 0 + + # Small delay to prevent busy-waiting + time.sleep(0.001) diff --git a/Not_A_Typewriter/Desktop_Not_A_Typewriter/keyboard_sender.py b/Not_A_Typewriter/Desktop_Not_A_Typewriter/keyboard_sender.py new file mode 100644 index 000000000..f8f3ab543 --- /dev/null +++ b/Not_A_Typewriter/Desktop_Not_A_Typewriter/keyboard_sender.py @@ -0,0 +1,223 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +#!/usr/bin/env python3 +""" +USB Typewriter Computer-side Script +Captures keyboard input and sends it to the Feather via serial +""" + +import struct +import time +import threading +import queue +import sys +import serial +import serial.tools.list_ports +from pynput import keyboard + +class TypewriterSender: + def __init__(self): + self.serial_port = None + self.key_queue = queue.Queue() + self.running = True + self.modifier_state = 0 + + # Map pynput keys to HID keycodes + self.key_to_hid = { + # Letters + 'a': 0x04, 'b': 0x05, 'c': 0x06, 'd': 0x07, + 'e': 0x08, 'f': 0x09, 'g': 0x0A, 'h': 0x0B, + 'i': 0x0C, 'j': 0x0D, 'k': 0x0E, 'l': 0x0F, + 'm': 0x10, 'n': 0x11, 'o': 0x12, 'p': 0x13, + 'q': 0x14, 'r': 0x15, 's': 0x16, 't': 0x17, + 'u': 0x18, 'v': 0x19, 'w': 0x1A, 'x': 0x1B, + 'y': 0x1C, 'z': 0x1D, + # Numbers + '1': 0x1E, '2': 0x1F, '3': 0x20, '4': 0x21, + '5': 0x22, '6': 0x23, '7': 0x24, '8': 0x25, + '9': 0x26, '0': 0x27, + # Special keys + keyboard.Key.enter: 0x28, + keyboard.Key.esc: 0x29, + keyboard.Key.backspace: 0x2A, + keyboard.Key.tab: 0x2B, + keyboard.Key.space: 0x2C, + '-': 0x2D, '=': 0x2E, '[': 0x2F, ']': 0x30, + '\\': 0x31, ';': 0x33, "'": 0x34, '`': 0x35, + ',': 0x36, '.': 0x37, '/': 0x38, + keyboard.Key.caps_lock: 0x39, + # Arrow keys + keyboard.Key.right: 0x4F, + keyboard.Key.left: 0x50, + keyboard.Key.down: 0x51, + keyboard.Key.up: 0x52, + } + + # Add function keys + for i in range(1, 13): + self.key_to_hid[getattr(keyboard.Key, f'f{i}')] = 0x3A + i - 1 + + # Modifier bits + self.modifier_bits = { + keyboard.Key.ctrl_l: 0x01, + keyboard.Key.shift_l: 0x02, + keyboard.Key.alt_l: 0x04, + keyboard.Key.cmd_l: 0x08, # Windows/Command key + keyboard.Key.ctrl_r: 0x10, + keyboard.Key.shift_r: 0x20, + keyboard.Key.alt_r: 0x40, + keyboard.Key.cmd_r: 0x80, + } + + @staticmethod + def find_feather_port(): + """Find the Feather's serial port""" + ports = serial.tools.list_ports.comports() + + print("Available serial ports:") + for i, port in enumerate(ports): + print(f"{i}: {port.device} - {port.description}") + feather_port = None + + if not feather_port: + # Manual selection + try: + choice = int(input("\nSelect port number: ")) + if 0 <= choice < len(ports): + feather_port = ports[choice].device + else: + print("Invalid selection") + return None + except (ValueError, IndexError): + print("Invalid input") + return None + + return feather_port + + def connect(self): + """Connect to the Feather via serial""" + port = self.find_feather_port() + if not port: + return False + + try: + self.serial_port = serial.Serial(port, 115200, timeout=0.1) + time.sleep(2) # Wait for connection to stabilize + print(f"Connected to {port}") + return True + except Exception as e: # pylint: disable=broad-except + print(f"Failed to connect: {e}") + return False + + def send_key_event(self, hid_code, pressed): + """Send a key event to the Feather""" + if self.serial_port and self.serial_port.is_open: + try: + # Protocol: [0xAA][modifier_byte][key_code][pressed] + # 0xAA is a start marker + data = struct.pack('BBBB', 0xAA, self.modifier_state, hid_code, 1 if pressed else 0) + self.serial_port.write(data) + self.serial_port.flush() + except Exception as e: # pylint: disable=broad-except + print(f"Error sending data: {e}") + + def on_press(self, key): + """Handle key press events""" + # Check for modifier keys + if key in self.modifier_bits: + self.modifier_state |= self.modifier_bits[key] + self.send_key_event(0, True) # Send modifier update + return + + # Get HID code for the key + hid_code = None + + # Check if it's a special key + if hasattr(key, 'value') and key in self.key_to_hid: + hid_code = self.key_to_hid[key] + # Check if it's a regular character + elif hasattr(key, 'char') and key.char: + hid_code = self.key_to_hid.get(key.char.lower()) + + if hid_code: + self.key_queue.put((hid_code, True)) + + def on_release(self, key): + """Handle key release events""" + # Check for modifier keys + if key in self.modifier_bits: + self.modifier_state &= ~self.modifier_bits[key] + self.send_key_event(0, False) # Send modifier update + return None + + # Get HID code for the key + hid_code = None + + # Check if it's a special key + if hasattr(key, 'value') and key in self.key_to_hid: + hid_code = self.key_to_hid[key] + # Check if it's a regular character + elif hasattr(key, 'char') and key.char: + hid_code = self.key_to_hid.get(key.char.lower()) + + if hid_code: + self.key_queue.put((hid_code, False)) + + # Check for escape to quit + if key == keyboard.Key.esc: + print("\nESC pressed - exiting...") + self.running = False + return False + + return None + + def process_queue(self): + """Process queued key events""" + while self.running: + try: + hid_code, pressed = self.key_queue.get(timeout=0.1) + self.send_key_event(hid_code, pressed) + + # Debug output + action = "pressed" if pressed else "released" + print(f"Key {action}: 0x{hid_code:02X}") + + except queue.Empty: + continue + + def run(self): + """Main run loop""" + if not self.connect(): + print("Failed to connect to Feather") + return + + print("\nNot A Typewriter") + print("Press keys to send to typewriter") + print("Press ESC to exit") + print("-" * 30) + + # Start queue processor thread + queue_thread = threading.Thread(target=self.process_queue) + queue_thread.daemon = True + queue_thread.start() + + # Start keyboard listener + with keyboard.Listener( + on_press=self.on_press, + on_release=self.on_release) as listener: + listener.join() + + # Cleanup + if self.serial_port: + self.serial_port.close() + print("Disconnected") + +if __name__ == "__main__": + try: + sender = TypewriterSender() + sender.run() + except KeyboardInterrupt: + print("\nInterrupted") + sys.exit(0) diff --git a/Not_A_Typewriter/USB_Host_Not_A_Typewriter/code.py b/Not_A_Typewriter/USB_Host_Not_A_Typewriter/code.py new file mode 100644 index 000000000..9ea2b36fc --- /dev/null +++ b/Not_A_Typewriter/USB_Host_Not_A_Typewriter/code.py @@ -0,0 +1,387 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import array +import time +import board +from adafruit_mcp230xx.mcp23017 import MCP23017 + +import usb +import adafruit_usb_host_descriptors +import usb_hid +from adafruit_hid.keyboard import Keyboard +from adafruit_hid.keycode import Keycode + +# Typewriter configuration +KEYSTROKE_BELL_INTERVAL = 25 # Ring bell every 25 keystrokes +SOLENOID_STRIKE_TIME = 0.03 # Duration in seconds for solenoid activation (reduced) +SOLENOID_DELAY = 0.01 # Small delay between solenoid operations (reduced) +ENTER_KEY_CODE = 0x28 # HID code for Enter key +ESCAPE_KEY_CODE = 0x29 # HID code for Escape key +BACKSPACE_KEY_CODE = 0x2A # HID code for Backspace key +TAB_KEY_CODE = 0x2B # HID code for Tab key +bell_keys = {ENTER_KEY_CODE, ESCAPE_KEY_CODE, TAB_KEY_CODE} + +# Set up USB HID keyboard +hid_keyboard = Keyboard(usb_hid.devices) + +# HID to Keycode mapping dictionary +hid_to_keycode = { + 0x04: Keycode.A, + 0x05: Keycode.B, + 0x06: Keycode.C, + 0x07: Keycode.D, + 0x08: Keycode.E, + 0x09: Keycode.F, + 0x0A: Keycode.G, + 0x0B: Keycode.H, + 0x0C: Keycode.I, + 0x0D: Keycode.J, + 0x0E: Keycode.K, + 0x0F: Keycode.L, + 0x10: Keycode.M, + 0x11: Keycode.N, + 0x12: Keycode.O, + 0x13: Keycode.P, + 0x14: Keycode.Q, + 0x15: Keycode.R, + 0x16: Keycode.S, + 0x17: Keycode.T, + 0x18: Keycode.U, + 0x19: Keycode.V, + 0x1A: Keycode.W, + 0x1B: Keycode.X, + 0x1C: Keycode.Y, + 0x1D: Keycode.Z, + 0x1E: Keycode.ONE, + 0x1F: Keycode.TWO, + 0x20: Keycode.THREE, + 0x21: Keycode.FOUR, + 0x22: Keycode.FIVE, + 0x23: Keycode.SIX, + 0x24: Keycode.SEVEN, + 0x25: Keycode.EIGHT, + 0x26: Keycode.NINE, + 0x27: Keycode.ZERO, + 0x28: Keycode.ENTER, + 0x29: Keycode.ESCAPE, + 0x2A: Keycode.BACKSPACE, + 0x2B: Keycode.TAB, + 0x2C: Keycode.SPACE, + 0x2D: Keycode.MINUS, + 0x2E: Keycode.EQUALS, + 0x2F: Keycode.LEFT_BRACKET, + 0x30: Keycode.RIGHT_BRACKET, + 0x31: Keycode.BACKSLASH, + 0x33: Keycode.SEMICOLON, + 0x34: Keycode.QUOTE, + 0x35: Keycode.GRAVE_ACCENT, + 0x36: Keycode.COMMA, + 0x37: Keycode.PERIOD, + 0x38: Keycode.FORWARD_SLASH, + 0x39: Keycode.CAPS_LOCK, + 0x3A: Keycode.F1, + 0x3B: Keycode.F2, + 0x3C: Keycode.F3, + 0x3D: Keycode.F4, + 0x3E: Keycode.F5, + 0x3F: Keycode.F6, + 0x40: Keycode.F7, + 0x41: Keycode.F8, + 0x42: Keycode.F9, + 0x43: Keycode.F10, + 0x44: Keycode.F11, + 0x45: Keycode.F12, + 0x4F: Keycode.RIGHT_ARROW, + 0x50: Keycode.LEFT_ARROW, + 0x51: Keycode.DOWN_ARROW, + 0x52: Keycode.UP_ARROW, +} + +# Modifier mapping +modifier_to_keycode = { + 0x01: Keycode.LEFT_CONTROL, + 0x02: Keycode.LEFT_SHIFT, + 0x04: Keycode.LEFT_ALT, + 0x08: Keycode.LEFT_GUI, + 0x10: Keycode.RIGHT_CONTROL, + 0x20: Keycode.RIGHT_SHIFT, + 0x40: Keycode.RIGHT_ALT, + 0x80: Keycode.RIGHT_GUI, +} + +#interface index, and endpoint addresses for USB Device instance +kbd_interface_index = None +kbd_endpoint_address = None +keyboard = None + +i2c = board.STEMMA_I2C() + +mcp = MCP23017(i2c) + +noid_2 = mcp.get_pin(0) # Key strike solenoid +noid_1 = mcp.get_pin(1) # Bell solenoid +noid_1.switch_to_output(value=False) +noid_2.switch_to_output(value=False) + +# Typewriter state tracking +keystroke_count = 0 +previous_keys = set() # Track previously pressed keys to detect new presses +previous_modifiers = 0 # Track modifier state + +#interface index, and endpoint addresses for USB Device instance +kbd_interface_index = None +kbd_endpoint_address = None +keyboard = None + +# scan for connected USB devices +for device in usb.core.find(find_all=True): + # check for boot keyboard endpoints on this device + kbd_interface_index, kbd_endpoint_address = ( + adafruit_usb_host_descriptors.find_boot_keyboard_endpoint(device) + ) + # if a boot keyboard interface index and endpoint address were found + if kbd_interface_index is not None and kbd_interface_index is not None: + keyboard = device + + # detach device from kernel if needed + if keyboard.is_kernel_driver_active(0): + keyboard.detach_kernel_driver(0) + + # set the configuration so it can be used + keyboard.set_configuration() + +if keyboard is None: + raise RuntimeError("No boot keyboard endpoint found") + +buf = array.array("b", [0] * 8) + +def strike_key_solenoid(): + """Activate the key strike solenoid briefly""" + noid_1.value = True + time.sleep(SOLENOID_STRIKE_TIME) + noid_1.value = False + +def ring_bell_solenoid(): + """Activate the bell solenoid briefly""" + noid_2.value = True + time.sleep(SOLENOID_STRIKE_TIME) + noid_2.value = False + +def get_pressed_keys(report_data): + """Extract currently pressed keys from HID report""" + pressed_keys = set() + + # Check bytes 2-7 for key codes (up to 6 simultaneous keys) + for i in range(2, 8): + k = report_data[i] + # Skip if no key (0) or error rollover (1) + if k > 1: + pressed_keys.add(k) + + return pressed_keys + +def print_keyboard_report(report_data): + # Dictionary for modifier keys (first byte) + modifier_dict = { + 0x01: "LEFT_CTRL", + 0x02: "LEFT_SHIFT", + 0x04: "LEFT_ALT", + 0x08: "LEFT_GUI", + 0x10: "RIGHT_CTRL", + 0x20: "RIGHT_SHIFT", + 0x40: "RIGHT_ALT", + 0x80: "RIGHT_GUI", + } + + # Dictionary for key codes (main keys) + key_dict = { + 0x04: "A", + 0x05: "B", + 0x06: "C", + 0x07: "D", + 0x08: "E", + 0x09: "F", + 0x0A: "G", + 0x0B: "H", + 0x0C: "I", + 0x0D: "J", + 0x0E: "K", + 0x0F: "L", + 0x10: "M", + 0x11: "N", + 0x12: "O", + 0x13: "P", + 0x14: "Q", + 0x15: "R", + 0x16: "S", + 0x17: "T", + 0x18: "U", + 0x19: "V", + 0x1A: "W", + 0x1B: "X", + 0x1C: "Y", + 0x1D: "Z", + 0x1E: "1", + 0x1F: "2", + 0x20: "3", + 0x21: "4", + 0x22: "5", + 0x23: "6", + 0x24: "7", + 0x25: "8", + 0x26: "9", + 0x27: "0", + 0x28: "ENTER", + 0x29: "ESC", + 0x2A: "BACKSPACE", + 0x2B: "TAB", + 0x2C: "SPACE", + 0x2D: "MINUS", + 0x2E: "EQUAL", + 0x2F: "LBRACKET", + 0x30: "RBRACKET", + 0x31: "BACKSLASH", + 0x33: "SEMICOLON", + 0x34: "QUOTE", + 0x35: "GRAVE", + 0x36: "COMMA", + 0x37: "PERIOD", + 0x38: "SLASH", + 0x39: "CAPS_LOCK", + 0x4F: "RIGHT_ARROW", + 0x50: "LEFT_ARROW", + 0x51: "DOWN_ARROW", + 0x52: "UP_ARROW", + } + + # Add F1-F12 keys to the dictionary + for i in range(12): + key_dict[0x3A + i] = f"F{i + 1}" + + # First byte contains modifier keys + modifiers = report_data[0] + + # Print modifier keys if pressed + if modifiers > 0: + print("Modifiers:", end=" ") + + # Check each bit for modifiers and print if pressed + for b, name in modifier_dict.items(): + if modifiers & b: + print(name, end=" ") + + print() + + # Bytes 2-7 contain up to 6 key codes (byte 1 is reserved) + keys_pressed = False + + for i in range(2, 8): + k = report_data[i] + + # Skip if no key or error rollover + if k in {0, 1}: + continue + + if not keys_pressed: + print("Keys:", end=" ") + keys_pressed = True + + # Print key name based on dictionary lookup + if k in key_dict: + print(key_dict[k], end=" ") + else: + # For keys not in the dictionary, print the HID code + print(f"0x{k:02X}", end=" ") + + if keys_pressed: + print() + elif modifiers == 0: + print("No keys pressed") + + +print("USB Typewriter starting...") +print(f"Bell will ring every {KEYSTROKE_BELL_INTERVAL} keystrokes or when Enter is pressed") + +while True: + # try to read data from the keyboard + try: + count = keyboard.read(kbd_endpoint_address, buf, timeout=10) + + # if there is no data it will raise USBTimeoutError + except usb.core.USBTimeoutError: + # Nothing to do if there is no data for this keyboard + continue + + # Get currently pressed keys and modifiers + current_keys = get_pressed_keys(buf) + current_modifiers = buf[0] + + # Find newly pressed keys (not in previous scan) + new_keys = current_keys - previous_keys + + # Find released keys for HID pass-through + released_keys = previous_keys - current_keys + + # Handle modifier changes + if current_modifiers != previous_modifiers: + # Build list of modifier keycodes to press/release + for bit, keycode in modifier_to_keycode.items(): + if current_modifiers & bit and not previous_modifiers & bit: + # Modifier newly pressed + hid_keyboard.press(keycode) + elif not (current_modifiers & bit) and (previous_modifiers & bit): + # Modifier released + hid_keyboard.release(keycode) + + # Release any keys that were let go + for key in released_keys: + if key in hid_to_keycode: + hid_keyboard.release(hid_to_keycode[key]) + + # Process each newly pressed key + for key in new_keys: + # Increment keystroke counter + keystroke_count += 1 + # Strike the key solenoid for typewriter effect + strike_key_solenoid() + # Pass through the key press via USB HID + if key in hid_to_keycode: + hid_keyboard.press(hid_to_keycode[key]) + + # Check if special keys were pressed + if key == ENTER_KEY_CODE: + ring_bell_solenoid() + keystroke_count = 0 # Reset counter for new line + elif key == ESCAPE_KEY_CODE: + ring_bell_solenoid() + keystroke_count = 0 # Reset counter + elif key == TAB_KEY_CODE: + ring_bell_solenoid() + keystroke_count = 0 # Reset counter + elif key == BACKSPACE_KEY_CODE: + keystroke_count = 0 # Reset counter but no bell + elif keystroke_count % KEYSTROKE_BELL_INTERVAL == 0: + print(f"\n*** DING! ({keystroke_count} keystrokes) ***\n") + ring_bell_solenoid() + # Special handling for bell keys that are still held + # check if they were released and re-pressed + # This handles rapid double-taps where the key might not fully release + + for key in bell_keys: + if key in current_keys and key in previous_keys and key not in new_keys: + # Key is being held, check if it was briefly released by looking at the raw state + # For held keys, we'll check if this is a repeat event + if len(current_keys) != len(previous_keys) or current_keys != previous_keys: + # Something changed, might be a repeat + continue + + # Update previous keys and modifiers for next scan + previous_keys = current_keys + previous_modifiers = current_modifiers + + # Still print the keyboard report for debugging + if new_keys: # Only print if there are new key presses + print_keyboard_report(buf) + print(f"Total keystrokes: {keystroke_count}") diff --git a/Pi_Hole_Ad_Blocker/mini_pitft_stats.py b/Pi_Hole_Ad_Blocker/mini_pitft_stats.py index 3d1349458..68e4576a9 100755 --- a/Pi_Hole_Ad_Blocker/mini_pitft_stats.py +++ b/Pi_Hole_Ad_Blocker/mini_pitft_stats.py @@ -1,7 +1,34 @@ # SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries +# SPDX-FileCopyrightText: 2025 Mikey Sklar for Adafruit Industries # # SPDX-License-Identifier: MIT +# Copyright (c) 2017 Adafruit Industries +# Author: Brent Rubell, Mikey Sklar +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +# This example is for use on (Linux) computers that are using CPython with +# Adafruit Blinka to support CircuitPython libraries. CircuitPython does +# not support PIL/pillow (python imaging library)! + + # -*- coding: utf-8 -*- # Import Python System Libraries import time @@ -19,11 +46,10 @@ import board from PIL import Image, ImageDraw, ImageFont import adafruit_rgb_display.st7789 as st7789 -API_TOKEN = "YOUR_API_TOKEN_HERE" -api_url = "http://localhost/admin/api.php?summaryRaw&auth="+API_TOKEN +API_URL = "http://localhost/api/stats/summary" # Configuration for CS and DC pins (these are FeatherWing defaults on M0/M4): -cs_pin = digitalio.DigitalInOut(board.CE0) +cs_pin = digitalio.DigitalInOut(board.D17) dc_pin = digitalio.DigitalInOut(board.D25) reset_pin = None @@ -34,54 +60,42 @@ BAUDRATE = 64000000 spi = board.SPI() # Create the ST7789 display: -disp = st7789.ST7789(spi, cs=cs_pin, dc=dc_pin, rst=reset_pin, baudrate=BAUDRATE, - width=135, height=240, x_offset=53, y_offset=40) +disp = st7789.ST7789( + spi, + dc_pin, + cs_pin, + reset_pin, + 135, + 240, + baudrate=BAUDRATE, + x_offset=53, + y_offset=40, + rotation=90 +) # Create blank image for drawing. # Make sure to create image with mode 'RGB' for full color. -height = disp.width # we swap height/width to rotate it to landscape! -width = disp.height -image = Image.new('RGB', (width, height)) -rotation = 90 - -# Get drawing object to draw on image. +CANVAS_WIDTH = disp.height +CANVAS_HEIGHT = disp.width +image = Image.new('RGB', (CANVAS_WIDTH, CANVAS_HEIGHT)) draw = ImageDraw.Draw(image) -# Draw a black filled box to clear the image. -draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0)) -disp.image(image, rotation) -# Draw some shapes. -# First define some constants to allow easy resizing of shapes. -padding = -2 -top = padding -bottom = height-padding -# Move left to right keeping track of the current x position for drawing shapes. -x = 0 +# Load default font (or replace with a TTF if desired) +FONT_PATH = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" +font = ImageFont.truetype(FONT_PATH, 24) - -# Alternatively load a TTF font. Make sure the .ttf font file is in the -# same directory as the python script! -# Some other nice fonts to try: http://www.dafont.com/bitmap.php -font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 24) - -# Turn on the backlight -backlight = digitalio.DigitalInOut(board.D22) -backlight.switch_to_output() -backlight.value = True - -# Add buttons as inputs buttonA = digitalio.DigitalInOut(board.D23) buttonA.switch_to_input() while True: # Draw a black filled box to clear the image. - draw.rectangle((0, 0, width, height), outline=0, fill=0) + draw.rectangle((0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), outline=0, fill=(0, 0, 0)) # Shell scripts for system monitoring from here: # https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load - cmd = "hostname -I | cut -d\' \' -f1" - IP = "IP: "+subprocess.check_output(cmd, shell=True).decode("utf-8") - cmd = "hostname | tr -d \'\\n\'" + cmd = "hostname -I | cut -d' ' -f1" + IP = "IP: " + subprocess.check_output(cmd, shell=True).decode("utf-8").strip() + cmd = "hostname | tr -d '\\n'" HOST = subprocess.check_output(cmd, shell=True).decode("utf-8") cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'" CPU = subprocess.check_output(cmd, shell=True).decode("utf-8") @@ -89,22 +103,27 @@ while True: MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8") cmd = "df -h | awk '$NF==\"/\"{printf \"Disk: %d/%d GB %s\", $3,$2,$5}'" Disk = subprocess.check_output(cmd, shell=True).decode("utf-8") - cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk \'{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}\'" # pylint: disable=line-too-long + cmd = ( + "cat /sys/class/thermal/thermal_zone0/temp | " + "awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'" + ) Temp = subprocess.check_output(cmd, shell=True).decode("utf-8") - # Pi Hole data! try: - r = requests.get(api_url) - data = json.loads(r.text) - DNSQUERIES = data['dns_queries_today'] - ADSBLOCKED = data['ads_blocked_today'] - CLIENTS = data['unique_clients'] - except KeyError: - time.sleep(1) - continue + r = requests.get(API_URL, timeout=5) + r.raise_for_status() + data = r.json() + DNSQUERIES = data["queries"]["total"] + ADSBLOCKED = data["queries"]["blocked"] + CLIENTS = data["clients"]["total"] + except (KeyError, requests.RequestException, json.JSONDecodeError): + DNSQUERIES = None + ADSBLOCKED = None + CLIENTS = None - y = top + y = top = 5 + x = 5 if not buttonA.value: # just button A pressed draw.text((x, y), IP, font=font, fill="#FFFF00") y += font.getbbox(IP)[3] @@ -121,13 +140,19 @@ while True: y += font.getbbox(IP)[3] draw.text((x, y), HOST, font=font, fill="#FFFF00") y += font.getbbox(HOST)[3] - draw.text((x, y), "Ads Blocked: {}".format(str(ADSBLOCKED)), font=font, fill="#00FF00") - y += font.getbbox(str(ADSBLOCKED))[3] - draw.text((x, y), "Clients: {}".format(str(CLIENTS)), font=font, fill="#0000FF") - y += font.getbbox(str(CLIENTS))[3] - draw.text((x, y), "DNS Queries: {}".format(str(DNSQUERIES)), font=font, fill="#FF00FF") - y += font.getbbox(str(DNSQUERIES))[3] + if ADSBLOCKED is not None: + txt = f"Ads Blocked: {ADSBLOCKED}" + draw.text((x, y), txt, font=font, fill="#00FF00") + y += font.getbbox(txt)[3] + if CLIENTS is not None: + txt = f"Clients: {CLIENTS}" + draw.text((x, y), txt, font=font, fill="#0000FF") + y += font.getbbox(txt)[3] + if DNSQUERIES is not None: + txt = f"DNS Queries: {DNSQUERIES}" + draw.text((x, y), txt, font=font, fill="#FF00FF") + y += font.getbbox(txt)[3] # Display image. - disp.image(image, rotation) + disp.image(image) time.sleep(.1) diff --git a/Pi_Hole_Ad_Blocker/stats.py b/Pi_Hole_Ad_Blocker/stats.py index d1021ba68..25e093404 100644 --- a/Pi_Hole_Ad_Blocker/stats.py +++ b/Pi_Hole_Ad_Blocker/stats.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2017 Limor Fried for Adafruit Industries # SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries # SPDX-FileCopyrightText: 2017 James DeVito for Adafruit Industries +# SPDX-FileCopyrightText: 2025 Mikey Sklar for Adafruit Industries # # SPDX-License-Identifier: MIT @@ -29,44 +30,33 @@ # Adafruit Blinka to support CircuitPython libraries. CircuitPython does # not support PIL/pillow (python imaging library)! -# Import Python System Libraries -import json + import subprocess import time -# Import Requests Library import requests - -# Import Blinka from board import SCL, SDA import busio import adafruit_ssd1306 - -# Import Python Imaging Library from PIL import Image, ImageDraw, ImageFont -API_TOKEN = "YOUR_API_TOKEN_HERE" -api_url = "http://localhost/admin/api.php?summaryRaw&auth="+API_TOKEN +api_url = "http://localhost/api/stats/summary" # Create the I2C interface. i2c = busio.I2C(SCL, SDA) # Create the SSD1306 OLED class. -# The first two parameters are the pixel width and pixel height. Change these -# to the right size for your display! disp = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c) # Leaving the OLED on for a long period of time can damage it -# Set these to prevent OLED burn in -DISPLAY_ON = 10 # on time in seconds -DISPLAY_OFF = 50 # off time in seconds +DISPLAY_ON = 10 # on time in seconds +DISPLAY_OFF = 50 # off time in seconds # Clear display. disp.fill(0) disp.show() # Create blank image for drawing. -# Make sure to create image with mode '1' for 1-bit color. width = disp.width height = disp.height image = Image.new('1', (width, height)) @@ -77,27 +67,21 @@ draw = ImageDraw.Draw(image) # Draw a black filled box to clear the image. draw.rectangle((0, 0, width, height), outline=0, fill=0) -# Draw some shapes. -# First define some constants to allow easy resizing of shapes. padding = -2 top = padding -bottom = height - padding -# Move left to right keeping track of the current x position -# for drawing shapes. x = 0 # Load nice silkscreen font font = ImageFont.truetype('/home/pi/slkscr.ttf', 8) while True: - # Draw a black filled box to clear the image. + # Clear the image buffer draw.rectangle((0, 0, width, height), outline=0, fill=0) - # Shell scripts for system monitoring from here : - # https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-load - cmd = "hostname -I | cut -d\' \' -f1 | tr -d \'\\n\'" + # Shell scripts for system monitoring + cmd = "hostname -I | cut -d' ' -f1 | tr -d '\\n'" IP = subprocess.check_output(cmd, shell=True).decode("utf-8") - cmd = "hostname | tr -d \'\\n\'" + cmd = "hostname | tr -d '\\n'" HOST = subprocess.check_output(cmd, shell=True).decode("utf-8") cmd = "top -bn1 | grep load | awk " \ "'{printf \"CPU Load: %.2f\", $(NF-2)}'" @@ -109,35 +93,30 @@ while True: "\"Disk: %d/%dGB %s\", $3,$2,$5}'" Disk = subprocess.check_output(cmd, shell=True).decode("utf-8") - # Pi Hole data! + # Pi-Hole data! try: - r = requests.get(api_url) - data = json.loads(r.text) - DNSQUERIES = data['dns_queries_today'] - ADSBLOCKED = data['ads_blocked_today'] - CLIENTS = data['unique_clients'] - except KeyError: - time.sleep(1) - continue + r = requests.get(api_url, timeout=2) + r.raise_for_status() + data = r.json() + DNSQUERIES = data["queries"]["total"] + ADSBLOCKED = data["queries"]["blocked"] + CLIENTS = data["clients"]["total"] + except (KeyError, requests.RequestException): + DNSQUERIES = 0 + ADSBLOCKED = 0 + CLIENTS = 0 - draw.text((x, top), "IP: " + str(IP) + - " (" + HOST + ")", font=font, fill=255) - draw.text((x, top + 8), "Ads Blocked: " + - str(ADSBLOCKED), font=font, fill=255) - draw.text((x, top + 16), "Clients: " + - str(CLIENTS), font=font, fill=255) - draw.text((x, top + 24), "DNS Queries: " + - str(DNSQUERIES), font=font, fill=255) - - # skip over original stats - # draw.text((x, top+8), str(CPU), font=font, fill=255) - # draw.text((x, top+16), str(MemUsage), font=font, fill=255) - # draw.text((x, top+25), str(Disk), font=font, fill=255) + draw.text((x, top), "IP: " + IP + " (" + HOST + ")", font=font, fill=255) + draw.text((x, top + 8), "Ads Blocked: " + str(ADSBLOCKED), font=font, fill=255) + draw.text((x, top + 16), "Clients: " + str(CLIENTS), font=font, fill=255) + draw.text((x, top + 24), "DNS Queries: " + str(DNSQUERIES), font=font, fill=255) # Display image. disp.image(image) disp.show() time.sleep(DISPLAY_ON) + + # Blank screen to prevent burn-in disp.fill(0) disp.show() time.sleep(DISPLAY_OFF) diff --git a/Sparkle_Motion_Examples/Arduino_Sparkle_Motion_Multi_NeoPixels/.feather_esp32_v2.test.only b/Sparkle_Motion_Examples/Arduino_Sparkle_Motion_Multi_NeoPixels/.feather_esp32_v2.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Sparkle_Motion_Examples/Arduino_Sparkle_Motion_Multi_NeoPixels/Arduino_Sparkle_Motion_Multi_NeoPixels.ino b/Sparkle_Motion_Examples/Arduino_Sparkle_Motion_Multi_NeoPixels/Arduino_Sparkle_Motion_Multi_NeoPixels.ino index af8651820..74c5334b7 100644 --- a/Sparkle_Motion_Examples/Arduino_Sparkle_Motion_Multi_NeoPixels/Arduino_Sparkle_Motion_Multi_NeoPixels.ino +++ b/Sparkle_Motion_Examples/Arduino_Sparkle_Motion_Multi_NeoPixels/Arduino_Sparkle_Motion_Multi_NeoPixels.ino @@ -23,7 +23,7 @@ uint16_t pixelHue_2 = 256; void loop() { pixelHue_1 += 256; - for(int i=0; i +#include "ESP_I2S.h" + +// I2S pin definitions for Sparklemotion +const uint8_t I2S_SCK = 14; // BCLK +const uint8_t I2S_WS = 12; // LRCLK +const uint8_t I2S_DIN = 13; // DATA_IN + +// Create I2S instance +I2SClass i2s; + +void setup() { + // Fast serial for plotting + Serial.begin(500000); + + // Initialize I2S + i2s.setPins(I2S_SCK, I2S_WS, -1, I2S_DIN); + if (!i2s.begin(I2S_MODE_STD, 44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO, I2S_STD_SLOT_LEFT)) { + Serial.println("Failed to initialize I2S bus!"); + return; + } + + Serial.println("I2S Mic Plotter Ready"); +} + +void loop() { + static uint32_t lastPlot = 0; + + // Get a sample + int32_t sample = i2s.read(); + + // Only plot every 1ms (1000 samples/sec is plenty for visualization) + if (millis() - lastPlot >= 1) { + if (sample >= 0) { // Valid sample + // Plot both raw and absolute values + Serial.printf("%d,%d\n", (int16_t)sample, abs((int16_t)sample)); + } + lastPlot = millis(); + } +} diff --git a/Sparkle_Motion_Stick_Examples/Arduino_Sparkle_Motion_Stick_IR_Remote/.feather_esp32_v2.test.only b/Sparkle_Motion_Stick_Examples/Arduino_Sparkle_Motion_Stick_IR_Remote/.feather_esp32_v2.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Sparkle_Motion_Stick_Examples/Arduino_Sparkle_Motion_Stick_IR_Remote/Arduino_Sparkle_Motion_Stick_IR_Remote.ino b/Sparkle_Motion_Stick_Examples/Arduino_Sparkle_Motion_Stick_IR_Remote/Arduino_Sparkle_Motion_Stick_IR_Remote.ino new file mode 100644 index 000000000..dac459c32 --- /dev/null +++ b/Sparkle_Motion_Stick_Examples/Arduino_Sparkle_Motion_Stick_IR_Remote/Arduino_Sparkle_Motion_Stick_IR_Remote.ino @@ -0,0 +1,88 @@ +// SPDX-FileCopyrightText: 2024 Liz Clark for Adafruit Industries +// SPDX-FileCopyrightText: 2025 Tim Cocks for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +/* + * Based on the SimpleReceiver.cpp and SimpleSender.cpp from the + * Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. + * by Armin Joachimsmeyer + ************************************************************************************ + * MIT License + * + * Copyright (c) 2020-2023 Armin Joachimsmeyer + * + */ + +#include + +#include // include the library +#include + +#define NEOPIXEL_STRIP_PIN 21 +#define NUM_PIXELS 8 + +#define IR_RECEIVE_PIN 10 + +Adafruit_NeoPixel NEOPIXEL_STRIP(NUM_PIXELS, NEOPIXEL_STRIP_PIN, NEO_GRB + NEO_KHZ800); + +uint8_t upCmd = 0x5; +uint8_t downCmd = 0xD; +uint8_t rightCmd = 0xA; +uint8_t leftCmd = 0x8; + +uint16_t pixelHue = 0; +uint8_t brightness = 25; + +void setup() { + Serial.begin(115200); + //while (!Serial); + Serial.println("Adafruit Sparkle Motion IR Remote Control NeoPixels Demo"); + IrReceiver.begin(IR_RECEIVE_PIN); + Serial.print("IRin on pin "); + Serial.print(IR_RECEIVE_PIN); + NEOPIXEL_STRIP.begin(); + NEOPIXEL_STRIP.setBrightness(25); +} + +void loop() { + /* + * Check if received data is available and if yes, try to decode it. + * When left or right buttons are pressed, change the pixelHue. + * When up or down buttons are pressed, change the brightness. + */ + if (IrReceiver.decode()) { + if (IrReceiver.decodedIRData.protocol == UNKNOWN) { + Serial.println("unknown"); + IrReceiver.printIRResultRawFormatted(&Serial, true); + IrReceiver.resume(); + } else { + IrReceiver.resume(); + //IrReceiver.printIRResultShort(&Serial); + + // Ignore repeat codes from holding down the button + if (IrReceiver.decodedIRData.flags == 0){ + //Serial.printf("Command: %d\n",IrReceiver.decodedIRData.command); + if (IrReceiver.decodedIRData.command == upCmd){ + Serial.println("UP btn"); + brightness = min(brightness + 25, 255); + }else if (IrReceiver.decodedIRData.command == downCmd){ + Serial.println("DOWN btn"); + brightness = max(brightness - 25, 0); + }else if (IrReceiver.decodedIRData.command == leftCmd){ + Serial.println("LEFT btn"); + pixelHue = (pixelHue - 8192) % 65536; + }else if (IrReceiver.decodedIRData.command == rightCmd){ + Serial.println("RIGHT btn"); + pixelHue = (pixelHue + 8192) % 65536; + } + + NEOPIXEL_STRIP.setBrightness(brightness); + NEOPIXEL_STRIP.fill(NEOPIXEL_STRIP.gamma32(NEOPIXEL_STRIP.ColorHSV(pixelHue))); + NEOPIXEL_STRIP.show(); + delay(100); + } + } + Serial.println(); + } +} diff --git a/TFT_Spirit_Board/esp32s3_s2_tft_featherwing_480x320/spirit_board.py b/TFT_Spirit_Board/esp32s3_s2_tft_featherwing_480x320/spirit_board.py index 9a889f608..af4e5d007 100644 --- a/TFT_Spirit_Board/esp32s3_s2_tft_featherwing_480x320/spirit_board.py +++ b/TFT_Spirit_Board/esp32s3_s2_tft_featherwing_480x320/spirit_board.py @@ -45,7 +45,7 @@ class SpiritBoard(displayio.Group): """ Create a SpiritBoard instance and put it in the displays root_group to make it visible. - :param displayio.Display display: Display object to show the spirit board on. + :param displayio.AnyDisplay display: Display object to show the spirit board on. """ self._display = display super().__init__() diff --git a/TFT_Spirit_Board/pyportal/spirit_board.py b/TFT_Spirit_Board/pyportal/spirit_board.py index 9a889f608..af4e5d007 100644 --- a/TFT_Spirit_Board/pyportal/spirit_board.py +++ b/TFT_Spirit_Board/pyportal/spirit_board.py @@ -45,7 +45,7 @@ class SpiritBoard(displayio.Group): """ Create a SpiritBoard instance and put it in the displays root_group to make it visible. - :param displayio.Display display: Display object to show the spirit board on. + :param displayio.AnyDisplay display: Display object to show the spirit board on. """ self._display = display super().__init__() diff --git a/TFT_Spirit_Board/pyportal_titano/spirit_board.py b/TFT_Spirit_Board/pyportal_titano/spirit_board.py index 9a889f608..af4e5d007 100644 --- a/TFT_Spirit_Board/pyportal_titano/spirit_board.py +++ b/TFT_Spirit_Board/pyportal_titano/spirit_board.py @@ -45,7 +45,7 @@ class SpiritBoard(displayio.Group): """ Create a SpiritBoard instance and put it in the displays root_group to make it visible. - :param displayio.Display display: Display object to show the spirit board on. + :param displayio.AnyDisplay display: Display object to show the spirit board on. """ self._display = display super().__init__() diff --git a/TMC2209_Camera_Slider/CircuitPython/Arial-14.bdf b/TMC2209_Camera_Slider/CircuitPython/Arial-14.bdf new file mode 100644 index 000000000..a92736e1a --- /dev/null +++ b/TMC2209_Camera_Slider/CircuitPython/Arial-14.bdf @@ -0,0 +1,27566 @@ +STARTFONT 2.1 +COMMENT +COMMENT Converted from OpenType font "arial.ttf" by "otf2bdf 3.0". +COMMENT +FONT -FreeType-Arial-Medium-R-Normal--19-140-100-100-P-94-ISO10646-1 +SIZE 14 100 100 +FONTBOUNDINGBOX 34 26 -13 -6 +STARTPROPERTIES 19 +FOUNDRY "FreeType" +FAMILY_NAME "Arial" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "" +PIXEL_SIZE 19 +POINT_SIZE 140 +RESOLUTION_X 100 +RESOLUTION_Y 100 +SPACING "P" +AVERAGE_WIDTH 94 +CHARSET_REGISTRY "ISO10646" +CHARSET_ENCODING "1" +FONT_ASCENT 17 +FONT_DESCENT 4 +COPYRIGHT "Typeface © The Monotype Corporation plc. Data © The Monotype Corporation plc/Type Solutions Inc. 1990-1992. All Rights Reserved" +_OTF_FONTFILE "arial.ttf" +_OTF_PSNAME "ArialMT" +ENDPROPERTIES +CHARS 1419 +STARTCHAR 0020 +ENCODING 32 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 0021 +ENCODING 33 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 14 2 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +80 +80 +80 +80 +00 +C0 +C0 +ENDCHAR +STARTCHAR 0022 +ENCODING 34 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 5 5 1 9 +BITMAP +D8 +D8 +D8 +D8 +90 +ENDCHAR +STARTCHAR 0023 +ENCODING 35 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +0880 +0880 +1880 +FFC0 +FFC0 +1100 +3100 +3100 +FFC0 +FFC0 +2200 +6200 +6200 +4600 +ENDCHAR +STARTCHAR 0024 +ENCODING 36 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 -2 +BITMAP +0800 +3E00 +7F00 +CB00 +C800 +C800 +7800 +3E00 +0F00 +0900 +0980 +C980 +CB00 +7F00 +3E00 +0800 +0800 +ENDCHAR +STARTCHAR 0025 +ENCODING 37 +SWIDTH 874 0 +DWIDTH 17 0 +BBX 15 14 1 0 +BITMAP +7830 +4C20 +CC40 +CC40 +CC80 +4880 +7900 +0338 +0264 +0646 +0446 +0C46 +0864 +1838 +ENDCHAR +STARTCHAR 0026 +ENCODING 38 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +1E00 +3F00 +3300 +2300 +3300 +1E00 +3C00 +7C00 +C6C0 +C3C0 +C180 +E3C0 +7FE0 +3C60 +ENDCHAR +STARTCHAR 0027 +ENCODING 39 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 5 1 9 +BITMAP +C0 +C0 +C0 +C0 +80 +ENDCHAR +STARTCHAR 0028 +ENCODING 40 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 18 1 -4 +BITMAP +10 +30 +20 +60 +60 +40 +C0 +C0 +C0 +C0 +C0 +C0 +40 +60 +60 +20 +30 +10 +ENDCHAR +STARTCHAR 0029 +ENCODING 41 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 18 1 -4 +BITMAP +C0 +40 +60 +20 +30 +10 +10 +18 +18 +18 +18 +10 +10 +30 +20 +60 +40 +C0 +ENDCHAR +STARTCHAR 002A +ENCODING 42 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 6 6 1 8 +BITMAP +20 +20 +FC +30 +50 +48 +ENDCHAR +STARTCHAR 002B +ENCODING 43 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 1 +BITMAP +0800 +0800 +0800 +0800 +FF80 +FF80 +0800 +0800 +0800 +0800 +ENDCHAR +STARTCHAR 002C +ENCODING 44 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 5 2 -3 +BITMAP +C0 +C0 +40 +80 +80 +ENDCHAR +STARTCHAR 002D +ENCODING 45 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 2 1 4 +BITMAP +F8 +F8 +ENDCHAR +STARTCHAR 002E +ENCODING 46 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 2 2 0 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR 002F +ENCODING 47 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 14 0 0 +BITMAP +08 +08 +18 +10 +10 +30 +20 +20 +20 +60 +40 +40 +C0 +80 +ENDCHAR +STARTCHAR 0030 +ENCODING 48 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3C00 +7E00 +6300 +C300 +C100 +C180 +C180 +C180 +C180 +C100 +C300 +E300 +7E00 +3C00 +ENDCHAR +STARTCHAR 0031 +ENCODING 49 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 5 14 2 0 +BITMAP +08 +18 +38 +F8 +98 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 0032 +ENCODING 50 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3E00 +7F00 +C300 +C180 +0180 +0300 +0300 +0600 +1C00 +3800 +6000 +C000 +FF80 +FF80 +ENDCHAR +STARTCHAR 0033 +ENCODING 51 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3C00 +7E00 +C300 +C300 +0300 +1E00 +1F00 +0300 +0180 +0180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 0034 +ENCODING 52 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +0300 +0700 +0700 +0F00 +1B00 +1300 +3300 +6300 +4300 +FFC0 +FFC0 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 0035 +ENCODING 53 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +7F00 +7F00 +6000 +4000 +4000 +DE00 +FF00 +C300 +0180 +0180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 0036 +ENCODING 54 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3E00 +7F00 +6300 +C100 +C000 +9E00 +BF00 +E300 +C180 +C180 +C180 +E300 +7F00 +3C00 +ENDCHAR +STARTCHAR 0037 +ENCODING 55 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +FF80 +FF80 +0300 +0600 +0600 +0C00 +0C00 +1800 +1800 +1000 +3000 +3000 +3000 +3000 +ENDCHAR +STARTCHAR 0038 +ENCODING 56 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3C00 +7E00 +6300 +C300 +C300 +6300 +3E00 +7E00 +C300 +C180 +C180 +C300 +7F00 +3E00 +ENDCHAR +STARTCHAR 0039 +ENCODING 57 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3C00 +7E00 +E300 +C100 +C180 +C180 +C380 +7F80 +3D80 +0180 +C300 +C300 +7E00 +3C00 +ENDCHAR +STARTCHAR 003A +ENCODING 58 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 10 2 0 +BITMAP +C0 +C0 +00 +00 +00 +00 +00 +00 +C0 +C0 +ENDCHAR +STARTCHAR 003B +ENCODING 59 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 13 2 -3 +BITMAP +C0 +C0 +00 +00 +00 +00 +00 +00 +C0 +C0 +40 +80 +80 +ENDCHAR +STARTCHAR 003C +ENCODING 60 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 9 1 2 +BITMAP +0080 +0380 +1F00 +7800 +C000 +7800 +1F00 +0380 +0080 +ENDCHAR +STARTCHAR 003D +ENCODING 61 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 6 1 4 +BITMAP +FF80 +FF80 +0000 +0000 +FF80 +FF80 +ENDCHAR +STARTCHAR 003E +ENCODING 62 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 9 1 2 +BITMAP +8000 +E000 +7C00 +0F00 +0180 +0F00 +7C00 +E000 +8000 +ENDCHAR +STARTCHAR 003F +ENCODING 63 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3E00 +7F00 +C300 +C180 +0180 +0300 +0600 +0C00 +0C00 +1800 +1800 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 0040 +ENCODING 64 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 17 18 1 -4 +BITMAP +03F000 +0FFC00 +1C0E00 +300300 +61D980 +47F980 +C63880 +CC1080 +8C1080 +883180 +883100 +CC7700 +CFFE00 +67BC00 +700180 +3C0700 +1FFE00 +03F800 +ENDCHAR +STARTCHAR 0041 +ENCODING 65 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 14 0 0 +BITMAP +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 0042 +ENCODING 66 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +FF00 +FF80 +C0C0 +C0C0 +C0C0 +C0C0 +FF80 +FF80 +C0C0 +C060 +C060 +C0C0 +FFC0 +FF80 +ENDCHAR +STARTCHAR 0043 +ENCODING 67 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 14 1 0 +BITMAP +0F80 +3FC0 +70E0 +6070 +C000 +C000 +C000 +C000 +C000 +C030 +6070 +70E0 +3FC0 +0F80 +ENDCHAR +STARTCHAR 0044 +ENCODING 68 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 14 1 0 +BITMAP +FF00 +FFC0 +C0E0 +C060 +C020 +C030 +C030 +C030 +C030 +C060 +C060 +C0E0 +FFC0 +FF00 +ENDCHAR +STARTCHAR 0045 +ENCODING 69 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 0046 +ENCODING 70 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 9 14 2 0 +BITMAP +FF80 +FF80 +8000 +8000 +8000 +8000 +FF00 +FF00 +8000 +8000 +8000 +8000 +8000 +8000 +ENDCHAR +STARTCHAR 0047 +ENCODING 71 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 14 1 0 +BITMAP +0F80 +3FE0 +7070 +6030 +C010 +C000 +C000 +C1F8 +C1F8 +C018 +6018 +7038 +3FF0 +0FC0 +ENDCHAR +STARTCHAR 0048 +ENCODING 72 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 10 14 2 0 +BITMAP +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +FFC0 +FFC0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +ENDCHAR +STARTCHAR 0049 +ENCODING 73 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 14 2 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 004A +ENCODING 74 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 7 14 1 0 +BITMAP +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +86 +C6 +FC +78 +ENDCHAR +STARTCHAR 004B +ENCODING 75 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +C0E0 +C1C0 +C380 +C700 +CE00 +DC00 +FC00 +F600 +E700 +C300 +C180 +C0C0 +C0E0 +C060 +ENDCHAR +STARTCHAR 004C +ENCODING 76 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +FF80 +FF80 +ENDCHAR +STARTCHAR 004D +ENCODING 77 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 13 14 1 0 +BITMAP +E018 +F038 +F028 +D028 +D868 +D868 +C848 +CCC8 +CCC8 +C488 +C788 +C788 +C308 +C308 +ENDCHAR +STARTCHAR 004E +ENCODING 78 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 1 0 +BITMAP +E060 +E060 +F060 +D860 +D860 +CC60 +CC60 +C660 +C360 +C360 +C1E0 +C0E0 +C0E0 +C060 +ENDCHAR +STARTCHAR 004F +ENCODING 79 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 14 1 0 +BITMAP +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 0050 +ENCODING 80 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +FF80 +FFC0 +C0E0 +C060 +C060 +C060 +C0C0 +FFC0 +FF80 +C000 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 0051 +ENCODING 81 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 15 1 -1 +BITMAP +0F80 +3FE0 +7070 +6030 +C038 +C018 +C018 +C018 +C018 +C030 +61B0 +70E0 +3FE0 +0FB8 +0008 +ENDCHAR +STARTCHAR 0052 +ENCODING 82 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 14 1 0 +BITMAP +FFC0 +FFE0 +C060 +C060 +C060 +C060 +FFE0 +FF80 +C300 +C180 +C0C0 +C060 +C060 +C030 +ENDCHAR +STARTCHAR 0053 +ENCODING 83 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +1F00 +7F80 +60C0 +C0C0 +C000 +7000 +3F00 +07C0 +00C0 +C060 +C060 +60C0 +7FC0 +1F00 +ENDCHAR +STARTCHAR 0054 +ENCODING 84 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 0 0 +BITMAP +FFE0 +FFE0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 0055 +ENCODING 85 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 1 0 +BITMAP +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 0056 +ENCODING 86 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 14 0 0 +BITMAP +C010 +6030 +6030 +6060 +3060 +3040 +10C0 +18C0 +1880 +0980 +0D80 +0D00 +0700 +0600 +ENDCHAR +STARTCHAR 0057 +ENCODING 87 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 18 14 0 0 +BITMAP +C0C0C0 +40C080 +61E180 +61E180 +612180 +233100 +333300 +323300 +321200 +161A00 +161E00 +1C0E00 +1C0C00 +0C0C00 +ENDCHAR +STARTCHAR 0058 +ENCODING 88 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 14 0 0 +BITMAP +6030 +3060 +30C0 +18C0 +0D80 +0F00 +0700 +0700 +0D80 +1980 +18C0 +3060 +6070 +E030 +ENDCHAR +STARTCHAR 0059 +ENCODING 89 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 14 0 0 +BITMAP +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 005A +ENCODING 90 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 0 0 +BITMAP +7FE0 +7FE0 +00C0 +01C0 +0180 +0300 +0600 +0C00 +1C00 +3800 +3000 +6000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 005B +ENCODING 91 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 18 1 -4 +BITMAP +F0 +F0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +F0 +F0 +ENDCHAR +STARTCHAR 005C +ENCODING 92 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 14 0 0 +BITMAP +80 +C0 +40 +40 +60 +20 +20 +20 +30 +10 +10 +18 +08 +08 +ENDCHAR +STARTCHAR 005D +ENCODING 93 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 18 0 -4 +BITMAP +F0 +F0 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +F0 +F0 +ENDCHAR +STARTCHAR 005E +ENCODING 94 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 7 1 7 +BITMAP +10 +38 +28 +6C +44 +C6 +82 +ENDCHAR +STARTCHAR 005F +ENCODING 95 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 2 0 -4 +BITMAP +FFE0 +FFE0 +ENDCHAR +STARTCHAR 0060 +ENCODING 96 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 3 3 1 11 +BITMAP +C0 +60 +20 +ENDCHAR +STARTCHAR 0061 +ENCODING 97 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 0062 +ENCODING 98 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +C000 +C000 +C000 +C000 +DE00 +FF00 +E300 +C180 +C180 +C180 +C180 +E300 +FF00 +DC00 +ENDCHAR +STARTCHAR 0063 +ENCODING 99 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +3C +7F +E3 +C0 +80 +80 +C0 +C3 +7F +3C +ENDCHAR +STARTCHAR 0064 +ENCODING 100 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +01 +01 +01 +01 +39 +7D +C3 +C3 +81 +81 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 0065 +ENCODING 101 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 0066 +ENCODING 102 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 14 0 0 +BITMAP +1C +3C +20 +20 +F8 +F8 +20 +20 +20 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR 0067 +ENCODING 103 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +3D +7F +C3 +C3 +81 +81 +C3 +C3 +7F +3D +01 +C3 +FE +7C +ENDCHAR +STARTCHAR 0068 +ENCODING 104 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +C0 +C0 +C0 +C0 +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +ENDCHAR +STARTCHAR 0069 +ENCODING 105 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 14 1 0 +BITMAP +C0 +C0 +00 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 006A +ENCODING 106 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 18 -1 -4 +BITMAP +30 +30 +00 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +F0 +E0 +ENDCHAR +STARTCHAR 006B +ENCODING 107 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +C0 +C0 +C0 +C0 +C7 +CE +DC +F8 +F8 +D8 +CC +C6 +C6 +C3 +ENDCHAR +STARTCHAR 006C +ENCODING 108 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 14 1 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 006D +ENCODING 109 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 10 1 0 +BITMAP +DC70 +FEF8 +E38C +C30C +C30C +C30C +C30C +C30C +C30C +C30C +ENDCHAR +STARTCHAR 006E +ENCODING 110 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 10 1 0 +BITMAP +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +ENDCHAR +STARTCHAR 006F +ENCODING 111 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 0070 +ENCODING 112 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 -4 +BITMAP +DE00 +FF00 +E300 +C180 +C180 +C180 +C180 +E300 +FF00 +DC00 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 0071 +ENCODING 113 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +39 +7D +C3 +C3 +81 +81 +C3 +C3 +7D +39 +01 +01 +01 +01 +ENDCHAR +STARTCHAR 0072 +ENCODING 114 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 10 1 0 +BITMAP +DC +F8 +E0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0073 +ENCODING 115 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +7C +FE +C6 +E0 +FC +3E +03 +C3 +FE +7C +ENDCHAR +STARTCHAR 0074 +ENCODING 116 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 13 0 0 +BITMAP +20 +20 +20 +F8 +F8 +20 +20 +20 +20 +20 +20 +38 +38 +ENDCHAR +STARTCHAR 0075 +ENCODING 117 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 10 1 0 +BITMAP +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 0076 +ENCODING 118 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 0 0 +BITMAP +C180 +6180 +6100 +2300 +3300 +3200 +1600 +1C00 +1C00 +0C00 +ENDCHAR +STARTCHAR 0077 +ENCODING 119 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 10 0 0 +BITMAP +C308 +4308 +4718 +6798 +6590 +24B0 +3CB0 +3CE0 +18E0 +1860 +ENDCHAR +STARTCHAR 0078 +ENCODING 120 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 0 0 +BITMAP +6180 +6300 +3600 +1E00 +0C00 +1C00 +1E00 +3300 +6300 +E180 +ENDCHAR +STARTCHAR 0079 +ENCODING 121 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 -4 +BITMAP +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +30 +F0 +E0 +ENDCHAR +STARTCHAR 007A +ENCODING 122 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 0 0 +BITMAP +7F80 +7F80 +0300 +0600 +0C00 +1800 +3000 +6000 +FF80 +FF80 +ENDCHAR +STARTCHAR 007B +ENCODING 123 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 18 1 -4 +BITMAP +38 +38 +60 +60 +60 +60 +60 +60 +C0 +C0 +60 +60 +60 +60 +60 +20 +38 +38 +ENDCHAR +STARTCHAR 007C +ENCODING 124 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 1 18 2 -4 +BITMAP +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR 007D +ENCODING 125 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 18 0 -4 +BITMAP +E0 +F0 +30 +30 +30 +30 +30 +18 +1C +1C +18 +30 +30 +30 +30 +30 +F0 +E0 +ENDCHAR +STARTCHAR 007E +ENCODING 126 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 4 1 4 +BITMAP +7000 +FC80 +8F80 +0700 +ENDCHAR +STARTCHAR 00A0 +ENCODING 160 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 00A1 +ENCODING 161 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 14 2 -4 +BITMAP +C0 +C0 +00 +40 +40 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 00A2 +ENCODING 162 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 18 1 -4 +BITMAP +02 +02 +02 +06 +3C +7F +E7 +C8 +C8 +C8 +D1 +F3 +7F +3C +20 +20 +20 +40 +ENDCHAR +STARTCHAR 00A3 +ENCODING 163 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +1F00 +3F80 +30C0 +6000 +6000 +2000 +FE00 +FE00 +3000 +3000 +2000 +7C40 +FFC0 +4380 +ENDCHAR +STARTCHAR 00A4 +ENCODING 164 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 8 1 2 +BITMAP +9D +FF +63 +C1 +C1 +63 +FF +9D +ENDCHAR +STARTCHAR 00A5 +ENCODING 165 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +C0C0 +60C0 +6180 +3180 +3300 +1B00 +1E00 +7FC0 +7FC0 +0C00 +7FC0 +7FC0 +0C00 +0C00 +ENDCHAR +STARTCHAR 00A6 +ENCODING 166 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 1 18 2 -4 +BITMAP +80 +80 +80 +80 +80 +80 +80 +80 +00 +00 +00 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR 00A7 +ENCODING 167 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 -4 +BITMAP +3C00 +7E00 +6300 +6000 +7000 +7C00 +8E00 +8300 +C180 +E180 +3900 +1E00 +0600 +0300 +C300 +6300 +7F00 +3C00 +ENDCHAR +STARTCHAR 00A8 +ENCODING 168 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 2 1 12 +BITMAP +98 +98 +ENDCHAR +STARTCHAR 00A9 +ENCODING 169 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 14 14 0 0 +BITMAP +0FC0 +1860 +2010 +4788 +CC4C +8804 +9804 +9804 +8844 +CCCC +4788 +2010 +1860 +0FC0 +ENDCHAR +STARTCHAR 00AA +ENCODING 170 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 6 7 0 7 +BITMAP +3C +44 +04 +7C +C4 +4C +7C +ENDCHAR +STARTCHAR 00AB +ENCODING 171 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 9 1 1 +BITMAP +13 +32 +26 +64 +CC +64 +26 +32 +13 +ENDCHAR +STARTCHAR 00AC +ENCODING 172 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 6 1 4 +BITMAP +FF80 +FF80 +0180 +0180 +0180 +0180 +ENDCHAR +STARTCHAR 00AD +ENCODING 173 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 2 1 4 +BITMAP +F8 +F8 +ENDCHAR +STARTCHAR 00AE +ENCODING 174 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 14 14 0 0 +BITMAP +0FC0 +1860 +2010 +4F88 +C8CC +88C4 +8F84 +8904 +8884 +C8CC +4848 +2010 +1860 +0FC0 +ENDCHAR +STARTCHAR 00AF +ENCODING 175 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 2 0 15 +BITMAP +FFE0 +FFE0 +ENDCHAR +STARTCHAR 00B0 +ENCODING 176 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 5 5 1 9 +BITMAP +70 +C8 +88 +C8 +70 +ENDCHAR +STARTCHAR 00B1 +ENCODING 177 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 11 1 0 +BITMAP +1800 +1800 +1800 +1800 +FF80 +FF80 +1800 +1800 +1800 +FF80 +FF80 +ENDCHAR +STARTCHAR 00B2 +ENCODING 178 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 7 0 7 +BITMAP +78 +4C +0C +18 +30 +60 +FC +ENDCHAR +STARTCHAR 00B3 +ENCODING 179 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 7 0 7 +BITMAP +78 +4C +0C +18 +0C +CC +78 +ENDCHAR +STARTCHAR 00B4 +ENCODING 180 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 3 3 2 11 +BITMAP +60 +40 +C0 +ENDCHAR +STARTCHAR 00B5 +ENCODING 181 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +C1 +C1 +C1 +C1 +C1 +C1 +C1 +E3 +FF +DD +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 00B6 +ENCODING 182 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 17 0 -3 +BITMAP +3FC0 +7FC0 +F980 +F980 +F980 +F980 +7980 +3980 +0980 +0980 +0980 +0980 +0980 +0980 +0980 +0980 +0980 +ENDCHAR +STARTCHAR 00B7 +ENCODING 183 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 2 2 6 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR 00B8 +ENCODING 184 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 4 1 -4 +BITMAP +60 +70 +30 +E0 +ENDCHAR +STARTCHAR 00B9 +ENCODING 185 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 3 7 1 7 +BITMAP +20 +60 +E0 +A0 +20 +20 +20 +ENDCHAR +STARTCHAR 00BA +ENCODING 186 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 7 0 7 +BITMAP +38 +44 +44 +C6 +44 +44 +38 +ENDCHAR +STARTCHAR 00BB +ENCODING 187 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 7 9 2 1 +BITMAP +98 +C8 +4C +66 +36 +66 +4C +C8 +98 +ENDCHAR +STARTCHAR 00BC +ENCODING 188 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 1 0 +BITMAP +2018 +6030 +E020 +A040 +20C0 +2080 +2100 +030C +061C +041C +082C +186C +307E +200C +ENDCHAR +STARTCHAR 00BD +ENCODING 189 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 1 0 +BITMAP +2010 +6030 +E020 +A040 +20C0 +2180 +2100 +023C +0664 +0C04 +080C +1018 +3030 +607E +ENDCHAR +STARTCHAR 00BE +ENCODING 190 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 16 14 0 0 +BITMAP +780C +4C18 +0C10 +1820 +0C60 +CC40 +7880 +0186 +030E +020E +0416 +0C36 +183F +1006 +ENDCHAR +STARTCHAR 00BF +ENCODING 191 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 9 14 1 -4 +BITMAP +0C00 +0C00 +0000 +0C00 +0C00 +0800 +1800 +3000 +6000 +4000 +C180 +6180 +7F00 +1E00 +ENDCHAR +STARTCHAR 00C0 +ENCODING 192 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0600 +0600 +0200 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 00C1 +ENCODING 193 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0300 +0200 +0600 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 00C2 +ENCODING 194 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0600 +0F00 +0980 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 00C3 +ENCODING 195 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0C80 +1F80 +1B80 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 00C4 +ENCODING 196 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 17 0 0 +BITMAP +0980 +0980 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 00C5 +ENCODING 197 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0600 +0900 +0900 +0900 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 00C6 +ENCODING 198 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 18 14 0 0 +BITMAP +03FFC0 +03FFC0 +066000 +066000 +0C6000 +0C6000 +087F80 +187F80 +1FE000 +3FE000 +306000 +606000 +607FC0 +C07FC0 +ENDCHAR +STARTCHAR 00C7 +ENCODING 199 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 -4 +BITMAP +0F80 +3FC0 +70E0 +6070 +C000 +C000 +C000 +C000 +C000 +C030 +6070 +70E0 +3FC0 +0F80 +0400 +0600 +0300 +0E00 +ENDCHAR +STARTCHAR 00C8 +ENCODING 200 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +0C00 +0C00 +0600 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 00C9 +ENCODING 201 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +0600 +0600 +0C00 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 00CA +ENCODING 202 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +0E00 +0A00 +1B00 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 00CB +ENCODING 203 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 17 1 0 +BITMAP +1B00 +1B00 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 00CC +ENCODING 204 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 3 18 1 0 +BITMAP +C0 +40 +60 +00 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 00CD +ENCODING 205 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 18 2 0 +BITMAP +C0 +C0 +80 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 00CE +ENCODING 206 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 18 0 0 +BITMAP +70 +58 +D8 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 00CF +ENCODING 207 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 17 0 0 +BITMAP +D8 +D8 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 00D0 +ENCODING 208 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 14 0 0 +BITMAP +7F80 +7FE0 +6070 +6030 +6010 +6018 +FE18 +FE18 +6018 +6030 +6030 +6070 +7FE0 +7F80 +ENDCHAR +STARTCHAR 00D1 +ENCODING 209 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0C80 +1F80 +1300 +0000 +E060 +E060 +F060 +D860 +D860 +CC60 +CC60 +C660 +C360 +C360 +C1E0 +C0E0 +C0E0 +C060 +ENDCHAR +STARTCHAR 00D2 +ENCODING 210 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0C00 +0600 +0200 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 00D3 +ENCODING 211 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0300 +0300 +0600 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 00D4 +ENCODING 212 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0700 +0D00 +0D80 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 00D5 +ENCODING 213 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0EC0 +0F80 +1B80 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 00D6 +ENCODING 214 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 17 1 0 +BITMAP +0D80 +0D80 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 00D7 +ENCODING 215 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 7 7 2 3 +BITMAP +82 +C6 +7C +38 +7C +C6 +82 +ENDCHAR +STARTCHAR 00D8 +ENCODING 216 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 15 1 -1 +BITMAP +0F88 +3FD0 +7060 +6070 +C0D0 +C198 +C318 +C618 +CC18 +D818 +7030 +7070 +7FE0 +DF80 +8000 +ENDCHAR +STARTCHAR 00D9 +ENCODING 217 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0C00 +0400 +0600 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 00DA +ENCODING 218 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0300 +0600 +0400 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 00DB +ENCODING 219 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0600 +0F00 +1900 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 00DC +ENCODING 220 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 17 1 0 +BITMAP +1900 +1900 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 00DD +ENCODING 221 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 18 0 0 +BITMAP +0300 +0300 +0600 +0000 +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 00DE +ENCODING 222 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +C000 +C000 +C000 +FF80 +FFC0 +C0E0 +C060 +C060 +C0E0 +FFC0 +FF80 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 00DF +ENCODING 223 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 10 14 1 0 +BITMAP +3C00 +7E00 +6300 +C300 +C200 +C600 +C400 +C600 +C380 +C1C0 +C0C0 +D8C0 +CFC0 +C780 +ENDCHAR +STARTCHAR 00E0 +ENCODING 224 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3800 +1800 +0800 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 00E1 +ENCODING 225 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 00E2 +ENCODING 226 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +1800 +3400 +2600 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 00E3 +ENCODING 227 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3200 +7E00 +4E00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 00E4 +ENCODING 228 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +2600 +2600 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 00E5 +ENCODING 229 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 16 1 0 +BITMAP +1800 +2400 +2400 +2400 +1800 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 00E6 +ENCODING 230 +SWIDTH 874 0 +DWIDTH 17 0 +BBX 15 10 1 0 +BITMAP +3E78 +7FFC +C386 +0F06 +7FFE +F3FE +8300 +C786 +FFFC +7878 +ENDCHAR +STARTCHAR 00E7 +ENCODING 231 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 -4 +BITMAP +3C +7F +E3 +C0 +80 +80 +C0 +C3 +7F +3C +10 +18 +0C +38 +ENDCHAR +STARTCHAR 00E8 +ENCODING 232 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3000 +1800 +0800 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 00E9 +ENCODING 233 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 00EA +ENCODING 234 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 00EB +ENCODING 235 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +2600 +2600 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 00EC +ENCODING 236 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 3 14 1 0 +BITMAP +C0 +C0 +60 +00 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 00ED +ENCODING 237 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 3 14 2 0 +BITMAP +60 +C0 +80 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 00EE +ENCODING 238 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 14 0 0 +BITMAP +30 +78 +C8 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 00EF +ENCODING 239 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 13 0 0 +BITMAP +C8 +C8 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 00F0 +ENCODING 240 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3200 +1E00 +3C00 +2600 +3B00 +7F00 +C300 +C180 +8180 +8180 +C180 +E300 +7F00 +3C00 +ENDCHAR +STARTCHAR 00F1 +ENCODING 241 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +3B +3E +6E +00 +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +ENDCHAR +STARTCHAR 00F2 +ENCODING 242 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3000 +1800 +0800 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 00F3 +ENCODING 243 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 00F4 +ENCODING 244 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 00F5 +ENCODING 245 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3200 +7E00 +4E00 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 00F6 +ENCODING 246 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +2600 +2600 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 00F7 +ENCODING 247 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 8 1 2 +BITMAP +1800 +1800 +0000 +FF80 +FF80 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 00F8 +ENCODING 248 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 9 12 1 -1 +BITMAP +0080 +1E80 +7F00 +6380 +C680 +C480 +C880 +D180 +7380 +7F00 +5E00 +8000 +ENDCHAR +STARTCHAR 00F9 +ENCODING 249 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +18 +18 +0C +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 00FA +ENCODING 250 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +0C +0C +18 +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 00FB +ENCODING 251 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +18 +3C +26 +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 00FC +ENCODING 252 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 13 1 0 +BITMAP +26 +26 +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 00FD +ENCODING 253 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 18 1 -4 +BITMAP +0C +08 +18 +00 +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +30 +F0 +E0 +ENDCHAR +STARTCHAR 00FE +ENCODING 254 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 -4 +BITMAP +C000 +C000 +C000 +C000 +DE00 +FF00 +E300 +C180 +C180 +C180 +C180 +E300 +FF00 +DC00 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 00FF +ENCODING 255 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 17 1 -4 +BITMAP +64 +64 +00 +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +30 +F0 +E0 +ENDCHAR +STARTCHAR 0100 +ENCODING 256 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 16 0 0 +BITMAP +1F80 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 0101 +ENCODING 257 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 1 0 +BITMAP +3E00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 0102 +ENCODING 258 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0880 +0980 +0700 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 0103 +ENCODING 259 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +2200 +2200 +1C00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 0104 +ENCODING 260 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 18 0 -4 +BITMAP +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +0010 +0020 +0030 +001C +ENDCHAR +STARTCHAR 0105 +ENCODING 261 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 14 1 -4 +BITMAP +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +0100 +0100 +0100 +01E0 +ENDCHAR +STARTCHAR 0106 +ENCODING 262 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 0 +BITMAP +0300 +0300 +0600 +0000 +0F80 +3FC0 +70E0 +6070 +C000 +C000 +C000 +C000 +C000 +C030 +6070 +70E0 +3FC0 +0F80 +ENDCHAR +STARTCHAR 0107 +ENCODING 263 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +0C +08 +18 +00 +3C +7F +E3 +C0 +80 +80 +C0 +C3 +7F +3C +ENDCHAR +STARTCHAR 0108 +ENCODING 264 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 0 +BITMAP +0600 +0F00 +0980 +0000 +0F80 +3FC0 +70E0 +6070 +C000 +C000 +C000 +C000 +C000 +C030 +6070 +70E0 +3FC0 +0F80 +ENDCHAR +STARTCHAR 0109 +ENCODING 265 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +1C +14 +36 +00 +3C +7F +E3 +C0 +80 +80 +C0 +C3 +7F +3C +ENDCHAR +STARTCHAR 010A +ENCODING 266 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 17 1 0 +BITMAP +0600 +0600 +0000 +0F80 +3FC0 +70E0 +6070 +C000 +C000 +C000 +C000 +C000 +C030 +6070 +70E0 +3FC0 +0F80 +ENDCHAR +STARTCHAR 010B +ENCODING 267 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 13 1 0 +BITMAP +18 +18 +00 +3C +7F +E3 +C0 +80 +80 +C0 +C3 +7F +3C +ENDCHAR +STARTCHAR 010C +ENCODING 268 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 0 +BITMAP +0D80 +0D00 +0700 +0000 +0F80 +3FC0 +70E0 +6070 +C000 +C000 +C000 +C000 +C000 +C030 +6070 +70E0 +3FC0 +0F80 +ENDCHAR +STARTCHAR 010D +ENCODING 269 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +26 +3C +18 +00 +3C +7F +E3 +C0 +80 +80 +C0 +C3 +7F +3C +ENDCHAR +STARTCHAR 010E +ENCODING 270 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 0 +BITMAP +3600 +1400 +1C00 +0000 +FF00 +FFC0 +C0E0 +C060 +C020 +C030 +C030 +C030 +C030 +C060 +C060 +C0E0 +FFC0 +FF00 +ENDCHAR +STARTCHAR 010F +ENCODING 271 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 1 0 +BITMAP +0160 +0160 +0120 +0140 +3D00 +7F00 +C300 +C300 +8100 +8100 +C300 +C300 +7D00 +3900 +ENDCHAR +STARTCHAR 0110 +ENCODING 272 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 14 0 0 +BITMAP +7F80 +7FE0 +6070 +6030 +6010 +6018 +FE18 +FE18 +6018 +6030 +6030 +6070 +7FE0 +7F80 +ENDCHAR +STARTCHAR 0111 +ENCODING 273 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 1 0 +BITMAP +0100 +1FC0 +1FC0 +0100 +3900 +7D00 +C300 +C300 +8100 +8100 +C300 +C300 +7D00 +3900 +ENDCHAR +STARTCHAR 0112 +ENCODING 274 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 16 1 0 +BITMAP +1F80 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 0113 +ENCODING 275 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 1 0 +BITMAP +7E00 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 0114 +ENCODING 276 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +1100 +1100 +0E00 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 0115 +ENCODING 277 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +2200 +2200 +1C00 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 0116 +ENCODING 278 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 17 1 0 +BITMAP +0600 +0600 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 0117 +ENCODING 279 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +1C00 +1C00 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 0118 +ENCODING 280 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 -4 +BITMAP +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +0100 +0100 +0100 +01E0 +ENDCHAR +STARTCHAR 0119 +ENCODING 281 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 -4 +BITMAP +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +0800 +0800 +0800 +0F00 +ENDCHAR +STARTCHAR 011A +ENCODING 282 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +1300 +1E00 +0C00 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 011B +ENCODING 283 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3600 +3400 +1C00 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 011C +ENCODING 284 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0300 +0780 +0CC0 +0000 +0F80 +3FE0 +7070 +6030 +C010 +C000 +C000 +C1F8 +C1F8 +C018 +6018 +7038 +3FF0 +0FC0 +ENDCHAR +STARTCHAR 011D +ENCODING 285 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 18 1 -4 +BITMAP +18 +3C +66 +00 +3D +7F +C3 +C3 +81 +81 +C3 +C3 +7F +3D +01 +C3 +FE +7C +ENDCHAR +STARTCHAR 011E +ENCODING 286 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0C40 +0480 +0780 +0000 +0F80 +3FE0 +7070 +6030 +C010 +C000 +C000 +C1F8 +C1F8 +C018 +6018 +7038 +3FF0 +0FC0 +ENDCHAR +STARTCHAR 011F +ENCODING 287 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 18 1 -4 +BITMAP +22 +26 +1C +00 +3D +7F +C3 +C3 +81 +81 +C3 +C3 +7F +3D +01 +C3 +FE +7C +ENDCHAR +STARTCHAR 0120 +ENCODING 288 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 17 1 0 +BITMAP +0300 +0300 +0000 +0F80 +3FE0 +7070 +6030 +C010 +C000 +C000 +C1F8 +C1F8 +C018 +6018 +7038 +3FF0 +0FC0 +ENDCHAR +STARTCHAR 0121 +ENCODING 289 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 17 1 -4 +BITMAP +18 +18 +00 +3D +7F +C3 +C3 +81 +81 +C3 +C3 +7F +3D +01 +C3 +FE +7C +ENDCHAR +STARTCHAR 0122 +ENCODING 290 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 -4 +BITMAP +0F80 +3FE0 +7070 +6030 +C010 +C000 +C000 +C1F8 +C1F8 +C018 +6018 +7038 +3FF0 +0FC0 +0300 +0380 +0180 +0700 +ENDCHAR +STARTCHAR 0123 +ENCODING 291 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 19 1 -4 +BITMAP +08 +10 +18 +18 +00 +3D +7F +C3 +C3 +81 +81 +C3 +C3 +7F +3D +03 +C3 +FE +7C +ENDCHAR +STARTCHAR 0124 +ENCODING 292 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 10 18 2 0 +BITMAP +0C00 +1E00 +1300 +0000 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +FFC0 +FFC0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +ENDCHAR +STARTCHAR 0125 +ENCODING 293 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 18 1 0 +BITMAP +0C +1E +33 +00 +C0 +C0 +C0 +C0 +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +ENDCHAR +STARTCHAR 0126 +ENCODING 294 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 14 0 0 +BITMAP +2030 +FFF8 +FFF8 +2030 +2030 +3FF0 +3FF0 +2030 +2030 +2030 +2030 +2030 +2030 +2030 +ENDCHAR +STARTCHAR 0127 +ENCODING 295 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 0 0 +BITMAP +6000 +FC00 +FC00 +6000 +6F00 +7F80 +7180 +6080 +6080 +6080 +6080 +6080 +6080 +6080 +ENDCHAR +STARTCHAR 0128 +ENCODING 296 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 7 18 -1 0 +BITMAP +76 +7C +DC +00 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 0129 +ENCODING 297 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 14 0 0 +BITMAP +EC +FC +98 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 012A +ENCODING 298 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 16 0 0 +BITMAP +FC +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 012B +ENCODING 299 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 12 0 0 +BITMAP +FC +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 012C +ENCODING 300 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 18 0 0 +BITMAP +88 +C8 +70 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 012D +ENCODING 301 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 14 0 0 +BITMAP +88 +C8 +70 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 012E +ENCODING 302 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 18 2 -4 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +80 +80 +80 +F0 +ENDCHAR +STARTCHAR 012F +ENCODING 303 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 18 1 -4 +BITMAP +C0 +C0 +00 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +80 +80 +80 +70 +ENDCHAR +STARTCHAR 0130 +ENCODING 304 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 17 2 0 +BITMAP +C0 +C0 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0131 +ENCODING 305 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 10 2 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0132 +ENCODING 306 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 2 0 +BITMAP +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C860 +CC60 +CFC0 +C780 +ENDCHAR +STARTCHAR 0133 +ENCODING 307 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 6 18 1 -4 +BITMAP +C4 +C4 +00 +00 +C4 +C4 +C4 +C4 +C4 +C4 +C4 +C4 +C4 +C4 +04 +04 +1C +1C +ENDCHAR +STARTCHAR 0134 +ENCODING 308 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 18 1 0 +BITMAP +0700 +0D00 +0D80 +0000 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +8600 +C600 +FC00 +7800 +ENDCHAR +STARTCHAR 0135 +ENCODING 309 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 6 18 -1 -4 +BITMAP +30 +78 +4C +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +F0 +E0 +ENDCHAR +STARTCHAR 0136 +ENCODING 310 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 -4 +BITMAP +C0E0 +C1C0 +C380 +C700 +CE00 +DC00 +FC00 +F600 +E700 +C300 +C180 +C0C0 +C0E0 +C060 +0000 +0700 +0100 +0F00 +ENDCHAR +STARTCHAR 0137 +ENCODING 311 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 18 1 -4 +BITMAP +C0 +C0 +C0 +C0 +C7 +CE +DC +F8 +F8 +D8 +CC +C6 +C6 +C3 +00 +0C +06 +1C +ENDCHAR +STARTCHAR 0138 +ENCODING 312 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +C7 +CE +D8 +F0 +F8 +C8 +CC +C6 +C6 +C3 +ENDCHAR +STARTCHAR 0139 +ENCODING 313 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +1800 +3000 +3000 +0000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +FF80 +FF80 +ENDCHAR +STARTCHAR 013A +ENCODING 314 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 18 1 0 +BITMAP +60 +C0 +80 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 013B +ENCODING 315 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 -4 +BITMAP +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +FF80 +FF80 +0000 +0E00 +0200 +1E00 +ENDCHAR +STARTCHAR 013C +ENCODING 316 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 18 0 -4 +BITMAP +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +00 +70 +30 +E0 +ENDCHAR +STARTCHAR 013D +ENCODING 317 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +C300 +C300 +C200 +C200 +C200 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +FF80 +FF80 +ENDCHAR +STARTCHAR 013E +ENCODING 318 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 14 1 0 +BITMAP +D8 +D8 +D0 +D0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 013F +ENCODING 319 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +C000 +C000 +C000 +C000 +C000 +C000 +C300 +C300 +C000 +C000 +C000 +C000 +FF80 +FF80 +ENDCHAR +STARTCHAR 0140 +ENCODING 320 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 14 1 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +D8 +D8 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0141 +ENCODING 321 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +6000 +6000 +6000 +6400 +6C00 +7800 +7000 +E000 +E000 +6000 +6000 +6000 +7FC0 +7FC0 +ENDCHAR +STARTCHAR 0142 +ENCODING 322 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 14 0 0 +BITMAP +60 +60 +60 +60 +70 +70 +60 +E0 +E0 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 0143 +ENCODING 323 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0600 +0600 +0400 +0000 +E060 +E060 +F060 +D860 +D860 +CC60 +CC60 +C660 +C360 +C360 +C1E0 +C0E0 +C0E0 +C060 +ENDCHAR +STARTCHAR 0144 +ENCODING 324 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +0C +0C +18 +00 +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +ENDCHAR +STARTCHAR 0145 +ENCODING 325 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 -4 +BITMAP +E060 +E060 +F060 +D860 +D860 +CC60 +CC60 +C660 +C360 +C360 +C1E0 +C0E0 +C0E0 +C060 +0000 +0700 +0180 +0700 +ENDCHAR +STARTCHAR 0146 +ENCODING 326 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +00 +1C +04 +3C +ENDCHAR +STARTCHAR 0147 +ENCODING 327 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +1900 +0F00 +0600 +0000 +E060 +E060 +F060 +D860 +D860 +CC60 +CC60 +C660 +C360 +C360 +C1E0 +C0E0 +C0E0 +C060 +ENDCHAR +STARTCHAR 0148 +ENCODING 328 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +36 +34 +1C +00 +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +ENDCHAR +STARTCHAR 0149 +ENCODING 329 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +C000 +C000 +4000 +8000 +B780 +3FC0 +38C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +ENDCHAR +STARTCHAR 014A +ENCODING 330 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 2 0 +BITMAP +9F00 +BF80 +E1C0 +C0C0 +C060 +8060 +8060 +8060 +8060 +8060 +80C0 +80C0 +8780 +8F00 +ENDCHAR +STARTCHAR 014B +ENCODING 331 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +01 +03 +07 +06 +ENDCHAR +STARTCHAR 014C +ENCODING 332 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 16 1 0 +BITMAP +0F80 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 014D +ENCODING 333 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 1 0 +BITMAP +7E00 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 014E +ENCODING 334 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0880 +0880 +0700 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 014F +ENCODING 335 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +2200 +2600 +1C00 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 0150 +ENCODING 336 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0CC0 +0D80 +0900 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 0151 +ENCODING 337 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +1B00 +3600 +2600 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 0152 +ENCODING 338 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 17 14 1 0 +BITMAP +1F7F80 +3FFF80 +71E000 +60E000 +C06000 +C06000 +C07F80 +C07F80 +C06000 +C06000 +60E000 +71E000 +3FFF80 +1F7F80 +ENDCHAR +STARTCHAR 0153 +ENCODING 339 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 16 10 1 0 +BITMAP +3C3C +7F7E +E3C3 +C183 +C1FF +C1FF +C180 +E3C3 +7F7E +3C3C +ENDCHAR +STARTCHAR 0154 +ENCODING 340 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 0 +BITMAP +0600 +0C00 +0800 +0000 +FFC0 +FFE0 +C060 +C060 +C060 +C060 +FFE0 +FF80 +C300 +C180 +C0C0 +C060 +C060 +C030 +ENDCHAR +STARTCHAR 0155 +ENCODING 341 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 14 1 0 +BITMAP +30 +30 +60 +00 +DC +F8 +E0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0156 +ENCODING 342 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 -4 +BITMAP +FFC0 +FFE0 +C060 +C060 +C060 +C060 +FFE0 +FF80 +C300 +C180 +C0C0 +C060 +C060 +C030 +0000 +0700 +0180 +0700 +ENDCHAR +STARTCHAR 0157 +ENCODING 343 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 14 1 -4 +BITMAP +DC +F8 +E0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +00 +70 +10 +F0 +ENDCHAR +STARTCHAR 0158 +ENCODING 344 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 0 +BITMAP +3300 +1E00 +0C00 +0000 +FFC0 +FFE0 +C060 +C060 +C060 +C060 +FFE0 +FF80 +C300 +C180 +C0C0 +C060 +C060 +C030 +ENDCHAR +STARTCHAR 0159 +ENCODING 345 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 14 1 0 +BITMAP +D8 +50 +70 +00 +DC +F8 +E0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 015A +ENCODING 346 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +0600 +0C00 +0800 +0000 +1F00 +7F80 +60C0 +C0C0 +C000 +7000 +3F00 +07C0 +00C0 +C060 +C060 +60C0 +7FC0 +1F00 +ENDCHAR +STARTCHAR 015B +ENCODING 347 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +0C +18 +10 +00 +7C +FE +C6 +E0 +FC +3E +03 +C3 +FE +7C +ENDCHAR +STARTCHAR 015C +ENCODING 348 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +0C00 +1A00 +1300 +0000 +1F00 +7F80 +60C0 +C0C0 +C000 +7000 +3F00 +07C0 +00C0 +C060 +C060 +60C0 +7FC0 +1F00 +ENDCHAR +STARTCHAR 015D +ENCODING 349 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +18 +3C +66 +00 +7C +FE +C6 +E0 +FC +3E +03 +C3 +FE +7C +ENDCHAR +STARTCHAR 015E +ENCODING 350 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 -4 +BITMAP +1F00 +7F80 +60C0 +C0C0 +C000 +7000 +3F00 +07C0 +00C0 +C060 +C060 +60C0 +7FC0 +1F00 +0400 +0E00 +0200 +1C00 +ENDCHAR +STARTCHAR 015F +ENCODING 351 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 -4 +BITMAP +7C +FE +C6 +E0 +FC +3E +03 +C3 +FE +7C +10 +18 +08 +38 +ENDCHAR +STARTCHAR 0160 +ENCODING 352 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +3300 +1E00 +0C00 +0000 +1F00 +7F80 +60C0 +C0C0 +C000 +7000 +3F00 +07C0 +00C0 +C060 +C060 +60C0 +7FC0 +1F00 +ENDCHAR +STARTCHAR 0161 +ENCODING 353 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +6C +2C +38 +00 +7C +FE +C6 +E0 +FC +3E +03 +C3 +FE +7C +ENDCHAR +STARTCHAR 0162 +ENCODING 354 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 19 0 -5 +BITMAP +FFE0 +FFE0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0000 +0600 +0600 +0200 +0400 +ENDCHAR +STARTCHAR 0163 +ENCODING 355 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 18 0 -5 +BITMAP +20 +20 +20 +F8 +F8 +20 +20 +20 +20 +20 +20 +38 +38 +00 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 0164 +ENCODING 356 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 18 0 0 +BITMAP +1900 +0F00 +0600 +0000 +FFE0 +FFE0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 0165 +ENCODING 357 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 14 0 0 +BITMAP +06 +26 +22 +22 +F8 +F8 +20 +20 +20 +20 +20 +20 +38 +38 +ENDCHAR +STARTCHAR 0166 +ENCODING 358 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 0 0 +BITMAP +FFE0 +FFE0 +0600 +0600 +0600 +0600 +0600 +3FC0 +3FC0 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 0167 +ENCODING 359 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 13 0 0 +BITMAP +20 +60 +60 +F8 +F8 +60 +60 +F8 +F8 +60 +60 +78 +38 +ENDCHAR +STARTCHAR 0168 +ENCODING 360 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0C80 +1F80 +1300 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 0169 +ENCODING 361 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +32 +7E +6E +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 016A +ENCODING 362 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 16 1 0 +BITMAP +1F80 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 016B +ENCODING 363 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 12 1 0 +BITMAP +7E +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 016C +ENCODING 364 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +1180 +0900 +0E00 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 016D +ENCODING 365 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +22 +26 +1C +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 016E +ENCODING 366 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0600 +0A00 +0900 +0A00 +C660 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 016F +ENCODING 367 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 16 1 0 +BITMAP +18 +24 +24 +24 +18 +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 0170 +ENCODING 368 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0CC0 +0D80 +1900 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 0171 +ENCODING 369 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +36 +36 +64 +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 0172 +ENCODING 370 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 -4 +BITMAP +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +0400 +0400 +0400 +0380 +ENDCHAR +STARTCHAR 0173 +ENCODING 371 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 1 -4 +BITMAP +C100 +C100 +C100 +C100 +C100 +C100 +C300 +C300 +7F00 +3D00 +0100 +0300 +0300 +01C0 +ENDCHAR +STARTCHAR 0174 +ENCODING 372 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 18 18 0 0 +BITMAP +00C000 +01E000 +033000 +000000 +C0C0C0 +40C080 +61E180 +61E180 +612180 +233100 +333300 +323300 +321200 +161A00 +161E00 +1C0E00 +1C0C00 +0C0C00 +ENDCHAR +STARTCHAR 0175 +ENCODING 373 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 14 0 0 +BITMAP +0300 +0780 +0CC0 +0000 +C308 +4308 +4718 +6798 +6590 +24B0 +3CB0 +3CE0 +18E0 +1860 +ENDCHAR +STARTCHAR 0176 +ENCODING 374 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 18 0 0 +BITMAP +0700 +0500 +0D80 +0000 +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 0177 +ENCODING 375 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 18 1 -4 +BITMAP +18 +3C +26 +00 +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +30 +F0 +E0 +ENDCHAR +STARTCHAR 0178 +ENCODING 376 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 17 0 0 +BITMAP +0980 +0980 +0000 +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 0179 +ENCODING 377 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 18 0 0 +BITMAP +0600 +0600 +0400 +0000 +7FE0 +7FE0 +00C0 +01C0 +0180 +0300 +0600 +0C00 +1C00 +3800 +3000 +6000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 017A +ENCODING 378 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 0 0 +BITMAP +0600 +0C00 +0800 +0000 +7F80 +7F80 +0300 +0600 +0C00 +1800 +3000 +6000 +FF80 +FF80 +ENDCHAR +STARTCHAR 017B +ENCODING 379 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 17 0 0 +BITMAP +0600 +0600 +0000 +7FE0 +7FE0 +00C0 +01C0 +0180 +0300 +0600 +0C00 +1C00 +3800 +3000 +6000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 017C +ENCODING 380 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 13 0 0 +BITMAP +0C00 +0C00 +0000 +7F80 +7F80 +0300 +0600 +0C00 +1800 +3000 +6000 +FF80 +FF80 +ENDCHAR +STARTCHAR 017D +ENCODING 381 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 18 0 0 +BITMAP +1900 +0F00 +0600 +0000 +7FE0 +7FE0 +00C0 +01C0 +0180 +0300 +0600 +0C00 +1C00 +3800 +3000 +6000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 017E +ENCODING 382 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 0 0 +BITMAP +3300 +1E00 +0C00 +0000 +7F80 +7F80 +0300 +0600 +0C00 +1800 +3000 +6000 +FF80 +FF80 +ENDCHAR +STARTCHAR 017F +ENCODING 383 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 5 14 1 0 +BITMAP +78 +F0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 018F +ENCODING 399 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 14 1 0 +BITMAP +0F80 +3FC0 +70E0 +6030 +6030 +0030 +FFF0 +FFF0 +C010 +C030 +6030 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 0192 +ENCODING 402 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 18 0 -4 +BITMAP +03C0 +03C0 +0200 +0600 +1F80 +1F80 +0600 +0400 +0400 +0C00 +0C00 +0C00 +0800 +0800 +1800 +1800 +7800 +F000 +ENDCHAR +STARTCHAR 01A0 +ENCODING 416 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 1 0 +BITMAP +0F86 +3FE6 +7072 +6034 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 01A1 +ENCODING 417 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 10 1 0 +BITMAP +3C60 +7F60 +C320 +C1C0 +81C0 +8180 +C180 +C300 +7F00 +3E00 +ENDCHAR +STARTCHAR 01AF +ENCODING 431 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 14 1 0 +BITMAP +C064 +C064 +C064 +C064 +C078 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 01B0 +ENCODING 432 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 10 1 0 +BITMAP +C160 +C160 +C120 +C120 +C1C0 +C100 +C300 +C300 +7D00 +3900 +ENDCHAR +STARTCHAR 01CD +ENCODING 461 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0980 +0F00 +0600 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 01CE +ENCODING 462 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3600 +1400 +1C00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 01CF +ENCODING 463 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 18 0 0 +BITMAP +D8 +58 +70 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 01D0 +ENCODING 464 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 5 14 0 0 +BITMAP +98 +F0 +60 +00 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 01D1 +ENCODING 465 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0D80 +0500 +0700 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 01D2 +ENCODING 466 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +2600 +3400 +1800 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 01D3 +ENCODING 467 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +1980 +0F00 +0600 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 01D4 +ENCODING 468 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +36 +34 +1C +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 01D5 +ENCODING 469 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0F00 +0000 +0900 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 01D6 +ENCODING 470 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 15 1 0 +BITMAP +7E +00 +26 +26 +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 01D7 +ENCODING 471 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0300 +0600 +0900 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 01D8 +ENCODING 472 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 17 1 0 +BITMAP +0C +0C +18 +00 +26 +26 +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 01D9 +ENCODING 473 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0F00 +0600 +0900 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 01DA +ENCODING 474 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 17 1 0 +BITMAP +36 +34 +1C +00 +26 +26 +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 01DB +ENCODING 475 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0C00 +0600 +0900 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 01DC +ENCODING 476 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 17 1 0 +BITMAP +30 +18 +08 +00 +26 +26 +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 01FA +ENCODING 506 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 20 0 0 +BITMAP +0300 +0300 +0600 +0900 +0900 +0900 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 01FB +ENCODING 507 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 20 1 0 +BITMAP +0600 +0C00 +0800 +0000 +1800 +2400 +2400 +2400 +1800 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 01FC +ENCODING 508 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 18 18 0 0 +BITMAP +006000 +006000 +00C000 +000000 +03FFC0 +03FFC0 +066000 +066000 +0C6000 +0C6000 +087F80 +187F80 +1FE000 +3FE000 +306000 +606000 +607FC0 +C07FC0 +ENDCHAR +STARTCHAR 01FD +ENCODING 509 +SWIDTH 874 0 +DWIDTH 17 0 +BBX 15 14 1 0 +BITMAP +00C0 +0180 +0100 +0000 +3E78 +7FFC +C386 +0F06 +7FFE +F3FE +8300 +C786 +FFFC +7878 +ENDCHAR +STARTCHAR 01FE +ENCODING 510 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 19 1 -1 +BITMAP +0300 +0300 +0200 +0000 +0F88 +3FD0 +7060 +6070 +C0D0 +C198 +C318 +C618 +CC18 +D818 +7030 +7070 +7FE0 +DF80 +8000 +ENDCHAR +STARTCHAR 01FF +ENCODING 511 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 9 15 1 -1 +BITMAP +0600 +0400 +0C00 +0080 +1E80 +7F00 +6380 +C680 +C480 +C880 +D180 +7380 +7F00 +5E00 +8000 +ENDCHAR +STARTCHAR 0259 +ENCODING 601 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3E00 +FF00 +C300 +0180 +FF80 +FF80 +C180 +C300 +7F00 +3E00 +ENDCHAR +STARTCHAR 02C6 +ENCODING 710 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 3 1 11 +BITMAP +60 +F0 +98 +ENDCHAR +STARTCHAR 02C7 +ENCODING 711 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 3 1 11 +BITMAP +D8 +D0 +70 +ENDCHAR +STARTCHAR 02C9 +ENCODING 713 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 1 0 11 +BITMAP +FC +ENDCHAR +STARTCHAR 02D8 +ENCODING 728 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 3 0 11 +BITMAP +C4 +4C +38 +ENDCHAR +STARTCHAR 02D9 +ENCODING 729 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 2 2 11 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR 02DA +ENCODING 730 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 5 1 9 +BITMAP +60 +90 +90 +90 +60 +ENDCHAR +STARTCHAR 02DB +ENCODING 731 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 4 2 -4 +BITMAP +80 +80 +80 +F0 +ENDCHAR +STARTCHAR 02DC +ENCODING 732 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 3 0 11 +BITMAP +64 +FC +9C +ENDCHAR +STARTCHAR 02DD +ENCODING 733 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 3 1 11 +BITMAP +EC +D8 +90 +ENDCHAR +STARTCHAR 0300 +ENCODING 768 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 3 3 -5 15 +BITMAP +C0 +60 +20 +ENDCHAR +STARTCHAR 0301 +ENCODING 769 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 3 3 -3 15 +BITMAP +60 +C0 +80 +ENDCHAR +STARTCHAR 0303 +ENCODING 771 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -8 15 +BITMAP +EC +98 +ENDCHAR +STARTCHAR 0309 +ENCODING 777 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 3 -4 15 +BITMAP +E0 +30 +60 +ENDCHAR +STARTCHAR 0323 +ENCODING 803 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -7 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR 037E +ENCODING 894 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 13 2 -3 +BITMAP +C0 +C0 +00 +00 +00 +00 +00 +00 +C0 +C0 +40 +80 +80 +ENDCHAR +STARTCHAR 0384 +ENCODING 900 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 3 3 2 11 +BITMAP +60 +40 +C0 +ENDCHAR +STARTCHAR 0385 +ENCODING 901 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 7 3 0 11 +BITMAP +18 +96 +A6 +ENDCHAR +STARTCHAR 0386 +ENCODING 902 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 14 0 0 +BITMAP +3600 +6700 +4F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 0387 +ENCODING 903 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 2 2 8 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR 0388 +ENCODING 904 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 14 -1 0 +BITMAP +67FE +67FE +C600 +0600 +0600 +0600 +07FC +07FC +0600 +0600 +0600 +0600 +07FE +07FE +ENDCHAR +STARTCHAR 0389 +ENCODING 905 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 -1 0 +BITMAP +6C06 +6C06 +CC06 +0C06 +0C06 +0C06 +0FFE +0FFE +0C06 +0C06 +0C06 +0C06 +0C06 +0C06 +ENDCHAR +STARTCHAR 038A +ENCODING 906 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 6 14 -1 0 +BITMAP +6C +6C +CC +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +0C +ENDCHAR +STARTCHAR 038C +ENCODING 908 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 14 -1 0 +BITMAP +63E0 +6FF8 +DC18 +180C +300C +3006 +3006 +3006 +3006 +300C +180C +1C18 +0FF8 +03E0 +ENDCHAR +STARTCHAR 038E +ENCODING 910 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 17 14 -1 0 +BITMAP +660180 +630300 +C30600 +018600 +018C00 +00D800 +007800 +007000 +003000 +003000 +003000 +003000 +003000 +003000 +ENDCHAR +STARTCHAR 038F +ENCODING 911 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 14 14 -1 0 +BITMAP +63E0 +6FF0 +DC38 +181C +300C +300C +300C +300C +300C +180C +0818 +0C30 +3E7C +3E7C +ENDCHAR +STARTCHAR 0390 +ENCODING 912 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 6 15 -1 0 +BITMAP +18 +94 +A4 +00 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 0391 +ENCODING 913 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 14 0 0 +BITMAP +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 0392 +ENCODING 914 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +FF00 +FF80 +C0C0 +C0C0 +C0C0 +C0C0 +FF80 +FF80 +C0C0 +C060 +C060 +C0C0 +FFC0 +FF80 +ENDCHAR +STARTCHAR 0393 +ENCODING 915 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 2 0 +BITMAP +FF +FF +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR 0394 +ENCODING 916 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 14 0 0 +BITMAP +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3060 +2060 +6060 +6030 +7FF0 +FFF8 +ENDCHAR +STARTCHAR 0395 +ENCODING 917 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 0396 +ENCODING 918 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 0 0 +BITMAP +7FE0 +7FE0 +00C0 +01C0 +0180 +0300 +0600 +0C00 +1C00 +3800 +3000 +6000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 0397 +ENCODING 919 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 10 14 2 0 +BITMAP +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +FFC0 +FFC0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +ENDCHAR +STARTCHAR 0398 +ENCODING 920 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 14 1 0 +BITMAP +0F80 +3FE0 +7070 +6030 +C018 +C018 +CF98 +CF98 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 0399 +ENCODING 921 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 14 2 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 039A +ENCODING 922 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +C0E0 +C1C0 +C380 +C700 +CE00 +DC00 +FC00 +F600 +E700 +C300 +C180 +C0C0 +C0E0 +C060 +ENDCHAR +STARTCHAR 039B +ENCODING 923 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 14 0 0 +BITMAP +0600 +0700 +0F00 +0D80 +0980 +1880 +18C0 +10C0 +3040 +3060 +6060 +6030 +4030 +C030 +ENDCHAR +STARTCHAR 039C +ENCODING 924 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 13 14 1 0 +BITMAP +E018 +F038 +F028 +D028 +D868 +D868 +C848 +CCC8 +CCC8 +C488 +C788 +C788 +C308 +C308 +ENDCHAR +STARTCHAR 039D +ENCODING 925 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 1 0 +BITMAP +E060 +E060 +F060 +D860 +D860 +CC60 +CC60 +C660 +C360 +C360 +C1E0 +C0E0 +C0E0 +C060 +ENDCHAR +STARTCHAR 039E +ENCODING 926 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 10 14 1 0 +BITMAP +FFC0 +FFC0 +0000 +0000 +0000 +0000 +7F80 +7F80 +0000 +0000 +0000 +0000 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 039F +ENCODING 927 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 14 1 0 +BITMAP +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 03A0 +ENCODING 928 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 10 14 2 0 +BITMAP +FFC0 +FFC0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +ENDCHAR +STARTCHAR 03A1 +ENCODING 929 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +FF80 +FFC0 +C0E0 +C060 +C060 +C060 +C0C0 +FFC0 +FF80 +C000 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 03A3 +ENCODING 931 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 10 14 1 0 +BITMAP +FFC0 +FFC0 +6000 +3000 +1800 +0C00 +0600 +0C00 +1800 +3800 +7000 +6000 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 03A4 +ENCODING 932 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 0 0 +BITMAP +FFE0 +FFE0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 03A5 +ENCODING 933 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 14 0 0 +BITMAP +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 03A6 +ENCODING 934 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 14 1 0 +BITMAP +0200 +0200 +1FC0 +7FF0 +E238 +C218 +C218 +C218 +C218 +E238 +7FF0 +1FC0 +0200 +0200 +ENDCHAR +STARTCHAR 03A7 +ENCODING 935 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 14 0 0 +BITMAP +6030 +3060 +30C0 +18C0 +0D80 +0F00 +0700 +0700 +0D80 +1980 +18C0 +3060 +6070 +E030 +ENDCHAR +STARTCHAR 03A8 +ENCODING 936 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 14 1 0 +BITMAP +C30C +C30C +C30C +C30C +C30C +C30C +E318 +7338 +3FF0 +1FE0 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 03A9 +ENCODING 937 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 14 1 0 +BITMAP +0F80 +3FC0 +70E0 +6070 +C030 +C030 +C030 +C030 +C030 +4020 +6060 +30C0 +F9F0 +F9F0 +ENDCHAR +STARTCHAR 03AA +ENCODING 938 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 17 0 0 +BITMAP +D8 +D8 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 03AB +ENCODING 939 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 17 0 0 +BITMAP +0980 +0980 +0000 +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 03AC +ENCODING 940 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +3D80 +7F80 +C380 +C300 +C100 +C100 +C300 +C380 +7F80 +3D80 +ENDCHAR +STARTCHAR 03AD +ENCODING 941 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 14 1 0 +BITMAP +18 +18 +10 +00 +78 +7C +C4 +C0 +70 +70 +C0 +C4 +FE +78 +ENDCHAR +STARTCHAR 03AE +ENCODING 942 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 18 1 -4 +BITMAP +0C +0C +08 +00 +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +01 +01 +01 +01 +ENDCHAR +STARTCHAR 03AF +ENCODING 943 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 14 1 0 +BITMAP +60 +40 +C0 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 03B0 +ENCODING 944 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +0C +4B +53 +00 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +E3 +7E +3C +ENDCHAR +STARTCHAR 03B1 +ENCODING 945 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3D80 +7F80 +C380 +C300 +C100 +C100 +C300 +C380 +7F80 +3D80 +ENDCHAR +STARTCHAR 03B2 +ENCODING 946 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 -4 +BITMAP +3E00 +7F00 +6300 +C100 +C300 +DC00 +DF00 +C380 +C180 +C180 +C180 +E380 +FF00 +DE00 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 03B3 +ENCODING 947 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 0 -4 +BITMAP +C180 +6180 +6100 +2300 +3300 +3200 +1600 +1E00 +1C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 03B4 +ENCODING 948 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +7F00 +7F00 +3000 +1800 +3E00 +7F00 +E300 +C180 +8180 +8180 +C180 +E300 +7F00 +3C00 +ENDCHAR +STARTCHAR 03B5 +ENCODING 949 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 10 1 0 +BITMAP +78 +7C +C4 +C0 +70 +70 +C0 +C4 +FE +78 +ENDCHAR +STARTCHAR 03B6 +ENCODING 950 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 18 1 -4 +BITMAP +7E +7E +0C +18 +30 +60 +40 +C0 +C0 +80 +80 +C0 +FC +7E +06 +06 +0C +0C +ENDCHAR +STARTCHAR 03B7 +ENCODING 951 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +01 +01 +01 +01 +ENDCHAR +STARTCHAR 03B8 +ENCODING 952 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3C00 +7E00 +6300 +C100 +C180 +C180 +FF80 +FF80 +C180 +C180 +C100 +6300 +7E00 +3C00 +ENDCHAR +STARTCHAR 03B9 +ENCODING 953 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 10 1 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 03BA +ENCODING 954 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +C7 +CE +D8 +F0 +F0 +D8 +CC +CC +C6 +C3 +ENDCHAR +STARTCHAR 03BB +ENCODING 955 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 0 0 +BITMAP +1000 +1800 +1800 +0800 +0C00 +1C00 +1E00 +1600 +3600 +3300 +6300 +6100 +6180 +C180 +ENDCHAR +STARTCHAR 03BC +ENCODING 956 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +C1 +C1 +C1 +C1 +C1 +C1 +C1 +E3 +FF +DD +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 03BD +ENCODING 957 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 0 0 +BITMAP +C180 +6180 +6100 +2300 +3300 +3200 +1600 +1C00 +1C00 +0C00 +ENDCHAR +STARTCHAR 03BE +ENCODING 958 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 18 1 -4 +BITMAP +3C +7C +C0 +C0 +40 +7C +1C +60 +C0 +C0 +C0 +C0 +7C +3E +06 +06 +0E +0C +ENDCHAR +STARTCHAR 03BF +ENCODING 959 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 03C0 +ENCODING 960 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 10 0 0 +BITMAP +FFF8 +FFF8 +1840 +1840 +1840 +1840 +1840 +1840 +1840 +1840 +ENDCHAR +STARTCHAR 03C1 +ENCODING 961 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 -4 +BITMAP +3E00 +7F00 +6380 +C180 +C180 +C180 +C180 +E380 +FF00 +DE00 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 03C2 +ENCODING 962 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 14 1 -4 +BITMAP +1F +3F +60 +C0 +C0 +80 +C0 +E0 +7C +3E +03 +02 +1E +1C +ENDCHAR +STARTCHAR 03C3 +ENCODING 963 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 10 10 1 0 +BITMAP +3FC0 +7FC0 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3E00 +ENDCHAR +STARTCHAR 03C4 +ENCODING 964 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 10 0 0 +BITMAP +FE +FE +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 03C5 +ENCODING 965 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +C3 +C3 +C3 +C3 +C3 +C3 +C3 +E3 +7E +3C +ENDCHAR +STARTCHAR 03C6 +ENCODING 966 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 1 -4 +BITMAP +3780 +6FC0 +CCC0 +CC60 +8C60 +8C60 +CCC0 +EDC0 +7F80 +3F00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 03C7 +ENCODING 967 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 14 0 -4 +BITMAP +6080 +6180 +3300 +3300 +1E00 +1E00 +0C00 +0C00 +1E00 +1600 +3300 +2100 +6180 +C0C0 +ENDCHAR +STARTCHAR 03C8 +ENCODING 968 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 1 -4 +BITMAP +C620 +C620 +C620 +C620 +C620 +C620 +C620 +C660 +66C0 +3F80 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 03C9 +ENCODING 969 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 10 1 0 +BITMAP +6030 +4010 +C218 +C218 +C218 +C218 +C618 +C538 +7DF0 +38E0 +ENDCHAR +STARTCHAR 03CA +ENCODING 970 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 5 13 0 0 +BITMAP +98 +98 +00 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 03CB +ENCODING 971 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 13 1 0 +BITMAP +36 +36 +00 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +E3 +7E +3C +ENDCHAR +STARTCHAR 03CC +ENCODING 972 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 03CD +ENCODING 973 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 0 +BITMAP +0C +0C +18 +00 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +E3 +7E +3C +ENDCHAR +STARTCHAR 03CE +ENCODING 974 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 14 1 0 +BITMAP +0180 +0300 +0200 +0000 +6030 +4010 +C218 +C218 +C218 +C218 +C618 +C538 +7DF0 +38E0 +ENDCHAR +STARTCHAR 0401 +ENCODING 1025 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 17 1 0 +BITMAP +1B00 +1B00 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 0402 +ENCODING 1026 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 16 14 0 0 +BITMAP +FFE0 +FFE0 +0600 +0600 +0600 +06F8 +07FE +0706 +0603 +0603 +0603 +0646 +067E +063C +ENDCHAR +STARTCHAR 0403 +ENCODING 1027 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 18 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +FF80 +FF80 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 0404 +ENCODING 1028 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 14 1 0 +BITMAP +0F80 +3FC0 +70E0 +6070 +C020 +C000 +FE00 +FE00 +C000 +C000 +6030 +7060 +3FE0 +0F80 +ENDCHAR +STARTCHAR 0405 +ENCODING 1029 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +1F00 +7F80 +60C0 +C0C0 +C000 +7000 +3F00 +07C0 +00C0 +C060 +C060 +60C0 +7FC0 +1F00 +ENDCHAR +STARTCHAR 0406 +ENCODING 1030 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 14 2 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0407 +ENCODING 1031 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 17 0 0 +BITMAP +D8 +D8 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 0408 +ENCODING 1032 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 7 14 1 0 +BITMAP +06 +06 +06 +06 +06 +06 +06 +06 +06 +06 +86 +C6 +FC +78 +ENDCHAR +STARTCHAR 0409 +ENCODING 1033 +SWIDTH 1028 0 +DWIDTH 20 0 +BBX 19 14 0 0 +BITMAP +3FE000 +3FE000 +306000 +306000 +306000 +306000 +307F80 +307FE0 +306060 +306060 +306060 +B06060 +F07FE0 +607F80 +ENDCHAR +STARTCHAR 040A +ENCODING 1034 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 16 14 2 0 +BITMAP +8300 +8300 +8300 +8300 +8300 +8300 +FFFE +FFFF +8303 +8301 +8301 +8303 +83FF +83FE +ENDCHAR +STARTCHAR 040B +ENCODING 1035 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 0 0 +BITMAP +FFE0 +FFE0 +0600 +0600 +0600 +06F8 +07FC +0706 +0606 +0602 +0602 +0602 +0602 +0602 +ENDCHAR +STARTCHAR 040C +ENCODING 1036 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 18 1 0 +BITMAP +0600 +0C00 +0C00 +0000 +C1C0 +C3C0 +C300 +C600 +C600 +CC00 +F800 +CC00 +C600 +C600 +C300 +C380 +C180 +C1C0 +ENDCHAR +STARTCHAR 040E +ENCODING 1038 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 18 0 0 +BITMAP +0880 +0880 +0700 +0000 +C030 +6020 +6060 +3040 +10C0 +1880 +0980 +0D00 +0700 +0600 +0600 +0600 +3C00 +3800 +ENDCHAR +STARTCHAR 040F +ENCODING 1039 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 -4 +BITMAP +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +FFE0 +FFE0 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 0410 +ENCODING 1040 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 14 0 0 +BITMAP +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 0411 +ENCODING 1041 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 10 14 2 0 +BITMAP +FF00 +FF00 +8000 +8000 +8000 +8000 +FF00 +FF80 +81C0 +80C0 +80C0 +81C0 +FF80 +FF00 +ENDCHAR +STARTCHAR 0412 +ENCODING 1042 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +FF00 +FF80 +C0C0 +C0C0 +C0C0 +C0C0 +FF80 +FF80 +C0C0 +C060 +C060 +C0C0 +FFC0 +FF80 +ENDCHAR +STARTCHAR 0413 +ENCODING 1043 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 1 0 +BITMAP +FF80 +FF80 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 0414 +ENCODING 1044 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 17 0 -3 +BITMAP +1FE0 +1FE0 +1060 +1060 +1060 +1060 +1060 +3060 +3060 +3060 +2060 +6060 +FFF0 +FFF0 +C010 +C010 +C010 +ENDCHAR +STARTCHAR 0415 +ENCODING 1045 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 0416 +ENCODING 1046 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 17 14 0 0 +BITMAP +E0C180 +F0C380 +30C600 +18C600 +18CC00 +0CDC00 +07F000 +0CDC00 +18CC00 +18C600 +30C700 +70C300 +60C380 +E0C180 +ENDCHAR +STARTCHAR 0417 +ENCODING 1047 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 1 0 +BITMAP +3E00 +7F00 +E380 +C180 +0180 +0380 +0F00 +0F80 +0180 +00C0 +C0C0 +E180 +7F80 +3E00 +ENDCHAR +STARTCHAR 0418 +ENCODING 1048 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 1 0 +BITMAP +C060 +C0E0 +C0E0 +C1A0 +C3A0 +C320 +C620 +CE20 +CC20 +D820 +D820 +F020 +E020 +E020 +ENDCHAR +STARTCHAR 0419 +ENCODING 1049 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +1100 +1900 +0E00 +0000 +C060 +C0E0 +C0E0 +C1A0 +C3A0 +C320 +C620 +CE20 +CC20 +D820 +D820 +F020 +E020 +E020 +ENDCHAR +STARTCHAR 041A +ENCODING 1050 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 1 0 +BITMAP +C1C0 +C3C0 +C300 +C600 +C600 +CC00 +F800 +CC00 +C600 +C600 +C300 +C380 +C180 +C1C0 +ENDCHAR +STARTCHAR 041B +ENCODING 1051 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 0 0 +BITMAP +3FE0 +3FE0 +3060 +3060 +3060 +3060 +3060 +3060 +3060 +3060 +3060 +B060 +F060 +6060 +ENDCHAR +STARTCHAR 041C +ENCODING 1052 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 13 14 1 0 +BITMAP +E018 +F038 +F028 +D028 +D868 +D868 +C848 +CCC8 +CCC8 +C488 +C788 +C788 +C308 +C308 +ENDCHAR +STARTCHAR 041D +ENCODING 1053 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 10 14 2 0 +BITMAP +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +FFC0 +FFC0 +80C0 +80C0 +80C0 +80C0 +80C0 +80C0 +ENDCHAR +STARTCHAR 041E +ENCODING 1054 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 14 1 0 +BITMAP +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 041F +ENCODING 1055 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 1 0 +BITMAP +FFE0 +FFE0 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +ENDCHAR +STARTCHAR 0420 +ENCODING 1056 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +FF80 +FFC0 +C0E0 +C060 +C060 +C060 +C0C0 +FFC0 +FF80 +C000 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 0421 +ENCODING 1057 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 14 1 0 +BITMAP +0F80 +3FC0 +70E0 +6070 +C000 +C000 +C000 +C000 +C000 +C030 +6070 +70E0 +3FC0 +0F80 +ENDCHAR +STARTCHAR 0422 +ENCODING 1058 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 0 0 +BITMAP +FFE0 +FFE0 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 0423 +ENCODING 1059 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 14 0 0 +BITMAP +C030 +6020 +6060 +3040 +10C0 +1880 +0980 +0D00 +0700 +0600 +0600 +0600 +3C00 +3800 +ENDCHAR +STARTCHAR 0424 +ENCODING 1060 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 14 1 0 +BITMAP +0600 +0600 +1FC0 +7FE0 +E670 +C630 +C618 +C618 +C630 +E670 +7FE0 +1FC0 +0600 +0600 +ENDCHAR +STARTCHAR 0425 +ENCODING 1061 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 14 0 0 +BITMAP +6030 +3060 +30C0 +18C0 +0D80 +0F00 +0700 +0700 +0D80 +1980 +18C0 +3060 +6070 +E030 +ENDCHAR +STARTCHAR 0426 +ENCODING 1062 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 -4 +BITMAP +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +FFF0 +FFF0 +0010 +0010 +0010 +0010 +ENDCHAR +STARTCHAR 0427 +ENCODING 1063 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 10 14 1 0 +BITMAP +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +C0C0 +E1C0 +7FC0 +3EC0 +00C0 +00C0 +00C0 +00C0 +ENDCHAR +STARTCHAR 0428 +ENCODING 1064 +SWIDTH 874 0 +DWIDTH 17 0 +BBX 15 14 1 0 +BITMAP +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +FFFE +FFFE +ENDCHAR +STARTCHAR 0429 +ENCODING 1065 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 16 18 1 -4 +BITMAP +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +C186 +FFFF +FFFF +0001 +0001 +0001 +0001 +ENDCHAR +STARTCHAR 042A +ENCODING 1066 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 14 14 0 0 +BITMAP +FC00 +FC00 +0C00 +0C00 +0C00 +0C00 +0FF0 +0FFC +0C0C +0C0C +0C0C +0C0C +0FFC +0FF0 +ENDCHAR +STARTCHAR 042B +ENCODING 1067 +SWIDTH 874 0 +DWIDTH 17 0 +BBX 13 14 2 0 +BITMAP +8018 +8018 +8018 +8018 +8018 +8018 +FF18 +FF98 +81D8 +80D8 +80D8 +81D8 +FF98 +FF18 +ENDCHAR +STARTCHAR 042C +ENCODING 1068 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 10 14 2 0 +BITMAP +8000 +8000 +8000 +8000 +8000 +8000 +FF00 +FF80 +81C0 +80C0 +80C0 +8180 +FF80 +FF00 +ENDCHAR +STARTCHAR 042D +ENCODING 1069 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 14 1 0 +BITMAP +1F00 +7FC0 +60C0 +C060 +4060 +0030 +07F0 +07F0 +0030 +0020 +C060 +E0E0 +7FC0 +1F00 +ENDCHAR +STARTCHAR 042E +ENCODING 1070 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 16 14 2 0 +BITMAP +80F8 +83FC +870E +8607 +8C03 +8C03 +FC03 +FC03 +8C03 +8C03 +8607 +870E +83FC +80F8 +ENDCHAR +STARTCHAR 042F +ENCODING 1071 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 1 0 +BITMAP +3FE0 +7FE0 +6060 +C060 +C060 +6060 +7FE0 +3FE0 +1C60 +3860 +3060 +6060 +C060 +C060 +ENDCHAR +STARTCHAR 0430 +ENCODING 1072 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 0431 +ENCODING 1073 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3F80 +7F00 +E000 +C000 +9E00 +BF00 +E380 +C180 +C180 +C180 +C180 +6380 +7F00 +3E00 +ENDCHAR +STARTCHAR 0432 +ENCODING 1074 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +FE +FF +C3 +C3 +FE +FF +C3 +C3 +FF +FE +ENDCHAR +STARTCHAR 0433 +ENCODING 1075 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 6 10 1 0 +BITMAP +FC +FC +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0434 +ENCODING 1076 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 13 0 -3 +BITMAP +1F80 +1F80 +3080 +3080 +3080 +3080 +2080 +6080 +FFE0 +FFE0 +8060 +8060 +8060 +ENDCHAR +STARTCHAR 0435 +ENCODING 1077 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 0436 +ENCODING 1078 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 10 0 0 +BITMAP +E630 +E670 +3660 +16C0 +0F80 +16C0 +3660 +6620 +6630 +C610 +ENDCHAR +STARTCHAR 0437 +ENCODING 1079 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 10 1 0 +BITMAP +78 +FC +C6 +06 +3C +3C +06 +86 +FE +7C +ENDCHAR +STARTCHAR 0438 +ENCODING 1080 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 10 1 0 +BITMAP +C3 +C3 +C7 +CD +C9 +D9 +D1 +F1 +E1 +C1 +ENDCHAR +STARTCHAR 0439 +ENCODING 1081 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +22 +22 +1C +00 +C3 +C3 +C7 +CD +C9 +D9 +D1 +F1 +E1 +C1 +ENDCHAR +STARTCHAR 043A +ENCODING 1082 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 10 1 0 +BITMAP +C6 +CE +CC +D8 +F0 +D8 +CC +CC +C6 +C6 +ENDCHAR +STARTCHAR 043B +ENCODING 1083 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 10 0 0 +BITMAP +3FC0 +3FC0 +30C0 +30C0 +30C0 +30C0 +30C0 +30C0 +F0C0 +E0C0 +ENDCHAR +STARTCHAR 043C +ENCODING 1084 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 10 1 0 +BITMAP +E0E0 +E0E0 +F1E0 +D1E0 +D160 +DB60 +CA60 +CA60 +CE60 +C460 +ENDCHAR +STARTCHAR 043D +ENCODING 1085 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 10 1 0 +BITMAP +C1 +C1 +C1 +C1 +FF +FF +C1 +C1 +C1 +C1 +ENDCHAR +STARTCHAR 043E +ENCODING 1086 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 043F +ENCODING 1087 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +FF +FF +C3 +C3 +C3 +C3 +C3 +C3 +C3 +C3 +ENDCHAR +STARTCHAR 0440 +ENCODING 1088 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 -4 +BITMAP +DE00 +FF00 +E300 +C180 +C180 +C180 +C180 +E300 +FF00 +DC00 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 0441 +ENCODING 1089 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +3C +7F +E3 +C0 +80 +80 +C0 +C3 +7F +3C +ENDCHAR +STARTCHAR 0442 +ENCODING 1090 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 10 0 0 +BITMAP +FF +FF +08 +08 +08 +08 +08 +08 +08 +08 +ENDCHAR +STARTCHAR 0443 +ENCODING 1091 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 -4 +BITMAP +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +30 +F0 +E0 +ENDCHAR +STARTCHAR 0444 +ENCODING 1092 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 18 1 -4 +BITMAP +0300 +0300 +0300 +0300 +3BF0 +7FF8 +C71C +C30C +830C +830C +C30C +C718 +7FF8 +3BF0 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 0445 +ENCODING 1093 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 0 0 +BITMAP +6180 +6300 +3600 +1E00 +0C00 +1C00 +1E00 +3300 +6300 +E180 +ENDCHAR +STARTCHAR 0446 +ENCODING 1094 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 -4 +BITMAP +C100 +C100 +C100 +C100 +C100 +C100 +C100 +C100 +FF80 +FF80 +0080 +0080 +0080 +0080 +ENDCHAR +STARTCHAR 0447 +ENCODING 1095 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +83 +83 +83 +83 +C3 +FF +7F +03 +03 +03 +ENDCHAR +STARTCHAR 0448 +ENCODING 1096 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 10 1 0 +BITMAP +C218 +C218 +C218 +C218 +C218 +C218 +C218 +C218 +FFF8 +FFF8 +ENDCHAR +STARTCHAR 0449 +ENCODING 1097 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 14 1 -4 +BITMAP +C218 +C218 +C218 +C218 +C218 +C218 +C218 +C218 +FFFC +FFFC +0004 +0004 +0004 +0004 +ENDCHAR +STARTCHAR 044A +ENCODING 1098 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 10 0 0 +BITMAP +F800 +F800 +1800 +1800 +1FC0 +1FE0 +1860 +1860 +1FE0 +1FC0 +ENDCHAR +STARTCHAR 044B +ENCODING 1099 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 10 1 0 +BITMAP +C020 +C020 +C020 +FE20 +FF20 +C320 +C120 +C320 +FF20 +FE20 +ENDCHAR +STARTCHAR 044C +ENCODING 1100 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +C0 +C0 +C0 +FE +FF +C3 +C1 +C3 +FF +FE +ENDCHAR +STARTCHAR 044D +ENCODING 1101 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +7C +FE +C7 +03 +1F +1F +03 +87 +FE +7C +ENDCHAR +STARTCHAR 044E +ENCODING 1102 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 10 1 0 +BITMAP +C3C0 +C7E0 +CE30 +CC10 +FC10 +FC10 +CC10 +CE30 +C7F0 +C3C0 +ENDCHAR +STARTCHAR 044F +ENCODING 1103 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +7F +FF +C3 +C3 +7F +1F +73 +63 +C3 +C3 +ENDCHAR +STARTCHAR 0451 +ENCODING 1105 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 0 +BITMAP +2600 +2600 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 0452 +ENCODING 1106 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 0 -4 +BITMAP +6000 +FC00 +FC00 +6000 +6F00 +7F80 +7180 +6080 +6080 +6080 +6080 +6080 +6080 +6080 +0080 +0180 +0380 +0300 +ENDCHAR +STARTCHAR 0453 +ENCODING 1107 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 6 14 1 0 +BITMAP +18 +18 +30 +00 +FC +FC +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0454 +ENCODING 1108 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +3C +7F +C3 +C0 +F8 +F8 +C0 +C3 +7F +3C +ENDCHAR +STARTCHAR 0455 +ENCODING 1109 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +7C +FE +C6 +E0 +FC +3E +03 +C3 +FE +7C +ENDCHAR +STARTCHAR 0456 +ENCODING 1110 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 14 1 0 +BITMAP +C0 +C0 +00 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0457 +ENCODING 1111 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 13 0 0 +BITMAP +C8 +C8 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR 0458 +ENCODING 1112 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 18 -1 -4 +BITMAP +30 +30 +00 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +F0 +E0 +ENDCHAR +STARTCHAR 0459 +ENCODING 1113 +SWIDTH 874 0 +DWIDTH 17 0 +BBX 17 10 0 0 +BITMAP +3FC000 +3FC000 +30C000 +30FE00 +30FF00 +30C300 +30C180 +30C300 +F0FF00 +E0FE00 +ENDCHAR +STARTCHAR 045A +ENCODING 1114 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 14 10 1 0 +BITMAP +C200 +C200 +C200 +C200 +FFF0 +FFF8 +C20C +C20C +C3F8 +C3F0 +ENDCHAR +STARTCHAR 045B +ENCODING 1115 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 0 0 +BITMAP +6000 +FC00 +FC00 +6000 +6F00 +7F80 +7180 +6080 +6080 +6080 +6080 +6080 +6080 +6080 +ENDCHAR +STARTCHAR 045C +ENCODING 1116 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 14 1 0 +BITMAP +18 +18 +30 +00 +C6 +CE +CC +D8 +F0 +D8 +CC +CC +C6 +C6 +ENDCHAR +STARTCHAR 045E +ENCODING 1118 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 18 1 -4 +BITMAP +46 +64 +38 +00 +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +30 +F0 +E0 +ENDCHAR +STARTCHAR 045F +ENCODING 1119 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +C1 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +FF +FF +08 +08 +08 +08 +ENDCHAR +STARTCHAR 0490 +ENCODING 1168 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 17 1 0 +BITMAP +03 +03 +03 +FF +FF +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0491 +ENCODING 1169 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 6 14 1 0 +BITMAP +04 +04 +04 +04 +FC +FC +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0492 +ENCODING 1170 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 14 0 0 +BITMAP +7FC0 +7FC0 +6000 +6000 +6000 +6000 +FE00 +FE00 +6000 +6000 +6000 +6000 +6000 +6000 +ENDCHAR +STARTCHAR 0493 +ENCODING 1171 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 10 0 0 +BITMAP +7E +7E +60 +F8 +F8 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 0496 +ENCODING 1174 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 17 18 0 -4 +BITMAP +E0C380 +F0C780 +38C600 +18CE00 +1CCC00 +0FF800 +07F800 +0CDC00 +18CE00 +38C600 +30C700 +70C300 +60C380 +E0C180 +000080 +000080 +000080 +000080 +ENDCHAR +STARTCHAR 0497 +ENCODING 1175 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 13 0 -3 +BITMAP +E630 +E670 +3660 +1FC0 +1FC0 +36E0 +3660 +6630 +6638 +C618 +0018 +0018 +0018 +ENDCHAR +STARTCHAR 049A +ENCODING 1178 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 18 1 -4 +BITMAP +C1C0 +C3C0 +C700 +C600 +CE00 +FC00 +FC00 +CE00 +C600 +C700 +C300 +C380 +C1C0 +C1C0 +00C0 +00C0 +00C0 +00C0 +ENDCHAR +STARTCHAR 049B +ENCODING 1179 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 13 1 -3 +BITMAP +C6 +CE +D8 +F8 +F8 +D8 +CC +CC +C6 +C6 +02 +02 +02 +ENDCHAR +STARTCHAR 049C +ENCODING 1180 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 1 0 +BITMAP +C1C0 +C3C0 +DF00 +DE00 +DE00 +FC00 +FC00 +DC00 +DE00 +DF00 +DB00 +DB80 +C180 +C1C0 +ENDCHAR +STARTCHAR 049D +ENCODING 1181 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 10 1 0 +BITMAP +C6 +DE +D8 +F8 +F8 +D8 +DC +DC +C6 +C6 +ENDCHAR +STARTCHAR 04A2 +ENCODING 1186 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 2 -4 +BITMAP +80C0 +80C0 +80C0 +80C0 +80C0 +FFC0 +FFC0 +80C0 +80C0 +80C0 +80C0 +80C0 +80E0 +80E0 +0020 +0020 +0020 +0020 +ENDCHAR +STARTCHAR 04A3 +ENCODING 1187 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 -3 +BITMAP +C100 +C100 +C100 +C100 +FF00 +FF00 +C100 +C100 +C180 +C180 +0080 +0080 +0080 +ENDCHAR +STARTCHAR 04AE +ENCODING 1198 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +C0C0 +60C0 +6180 +3180 +3300 +1A00 +0E00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 04AF +ENCODING 1199 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 0 -4 +BITMAP +C180 +6180 +6100 +6300 +3300 +3600 +1600 +1E00 +1C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 04B0 +ENCODING 1200 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +C0C0 +60C0 +2180 +3100 +1B00 +1E00 +7FC0 +7FC0 +0C00 +0C00 +0C00 +0C00 +0C00 +0C00 +ENDCHAR +STARTCHAR 04B1 +ENCODING 1201 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 0 -4 +BITMAP +C180 +6180 +6100 +6300 +3300 +3600 +1600 +1E00 +1C00 +0C00 +7F80 +7F80 +0C00 +0C00 +ENDCHAR +STARTCHAR 04B2 +ENCODING 1202 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 -4 +BITMAP +6030 +3060 +38C0 +18C0 +0D80 +0F00 +0700 +0700 +0D80 +1DC0 +18C0 +3060 +6078 +E038 +0018 +0018 +0018 +0018 +ENDCHAR +STARTCHAR 04B3 +ENCODING 1203 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 13 0 -3 +BITMAP +6180 +7300 +3600 +1E00 +0C00 +1E00 +1E00 +3300 +6380 +E180 +0080 +0080 +0080 +ENDCHAR +STARTCHAR 04B8 +ENCODING 1208 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 10 14 1 0 +BITMAP +C0C0 +C0C0 +C0C0 +CCC0 +CCC0 +CCC0 +EDC0 +7FC0 +3EC0 +0CC0 +0CC0 +0CC0 +00C0 +00C0 +ENDCHAR +STARTCHAR 04B9 +ENCODING 1209 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 0 +BITMAP +83 +83 +93 +D3 +FF +7F +13 +13 +03 +03 +ENDCHAR +STARTCHAR 04BA +ENCODING 1210 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +C000 +C000 +C000 +C000 +C000 +DF80 +FFC0 +E0E0 +C060 +C060 +C060 +C060 +C060 +C060 +ENDCHAR +STARTCHAR 04BB +ENCODING 1211 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +C0 +C0 +C0 +C0 +DE +FF +E3 +C1 +C1 +C1 +C1 +C1 +C1 +C1 +ENDCHAR +STARTCHAR 04D8 +ENCODING 1240 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 14 1 0 +BITMAP +0F80 +3FC0 +70E0 +6030 +6030 +0030 +FFF0 +FFF0 +C010 +C030 +6030 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 04D9 +ENCODING 1241 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3E00 +FF00 +C300 +0180 +FF80 +FF80 +C180 +C300 +7F00 +3E00 +ENDCHAR +STARTCHAR 04E8 +ENCODING 1256 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 14 1 0 +BITMAP +0F80 +3FE0 +7070 +6030 +C018 +C018 +FFF8 +FFF8 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 04E9 +ENCODING 1257 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3E00 +7F00 +C300 +C180 +FF80 +FF80 +8180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 05B0 +ENCODING 1456 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 3 2 -4 +BITMAP +C0 +00 +C0 +ENDCHAR +STARTCHAR 05B1 +ENCODING 1457 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 3 0 -4 +BITMAP +FC +00 +6C +ENDCHAR +STARTCHAR 05B2 +ENCODING 1458 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 3 0 -4 +BITMAP +FC +00 +0C +ENDCHAR +STARTCHAR 05B3 +ENCODING 1459 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 3 0 -4 +BITMAP +FC +60 +0C +ENDCHAR +STARTCHAR 05B4 +ENCODING 1460 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 1 2 -3 +BITMAP +C0 +ENDCHAR +STARTCHAR 05B5 +ENCODING 1461 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 1 1 -3 +BITMAP +D0 +ENDCHAR +STARTCHAR 05B6 +ENCODING 1462 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 3 1 -4 +BITMAP +D0 +00 +60 +ENDCHAR +STARTCHAR 05B7 +ENCODING 1463 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 1 1 -3 +BITMAP +F0 +ENDCHAR +STARTCHAR 05B8 +ENCODING 1464 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 3 1 -4 +BITMAP +F0 +20 +20 +ENDCHAR +STARTCHAR 05B9 +ENCODING 1465 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 1 2 11 +BITMAP +C0 +ENDCHAR +STARTCHAR 05BA +ENCODING 1466 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 1 2 11 +BITMAP +C0 +ENDCHAR +STARTCHAR 05BB +ENCODING 1467 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 3 0 -4 +BITMAP +C0 +30 +04 +ENDCHAR +STARTCHAR 05BC +ENCODING 1468 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 1 2 5 +BITMAP +C0 +ENDCHAR +STARTCHAR 05BD +ENCODING 1469 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 1 3 3 -4 +BITMAP +80 +80 +80 +ENDCHAR +STARTCHAR 05BE +ENCODING 1470 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 5 2 1 8 +BITMAP +F8 +F8 +ENDCHAR +STARTCHAR 05BF +ENCODING 1471 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 1 1 12 +BITMAP +F0 +ENDCHAR +STARTCHAR 05C0 +ENCODING 1472 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 1 12 2 0 +BITMAP +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR 05C1 +ENCODING 1473 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 1 7 12 +BITMAP +C0 +ENDCHAR +STARTCHAR 05C2 +ENCODING 1474 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 1 1 -2 12 +BITMAP +80 +ENDCHAR +STARTCHAR 05C3 +ENCODING 1475 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 10 2 0 +BITMAP +C0 +C0 +00 +00 +00 +00 +00 +00 +C0 +C0 +ENDCHAR +STARTCHAR 05D0 +ENCODING 1488 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +E180 +6100 +3300 +3B00 +7F00 +4E00 +C600 +C600 +C300 +C180 +ENDCHAR +STARTCHAR 05D1 +ENCODING 1489 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 10 0 0 +BITMAP +7C00 +7E00 +0700 +0300 +0100 +0100 +0100 +0100 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 05D2 +ENCODING 1490 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 10 0 0 +BITMAP +78 +7C +0C +0C +0C +1C +3C +64 +66 +C6 +ENDCHAR +STARTCHAR 05D3 +ENCODING 1491 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 0 0 +BITMAP +FF80 +FF80 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 05D4 +ENCODING 1492 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +FE00 +7F00 +0380 +0180 +C180 +C180 +C180 +C180 +C180 +C180 +ENDCHAR +STARTCHAR 05D5 +ENCODING 1493 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 10 1 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 05D6 +ENCODING 1494 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 6 10 1 0 +BITMAP +FC +FC +30 +60 +60 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 05D7 +ENCODING 1495 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +FE00 +FF00 +C380 +C180 +C180 +C180 +C180 +C180 +C180 +C180 +ENDCHAR +STARTCHAR 05D8 +ENCODING 1496 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +C700 +CF80 +C980 +C180 +C180 +C180 +C180 +6380 +7F00 +1E00 +ENDCHAR +STARTCHAR 05D9 +ENCODING 1497 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 5 1 5 +BITMAP +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 05DA +ENCODING 1498 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 0 -4 +BITMAP +FC +FE +07 +03 +01 +01 +01 +01 +01 +01 +01 +01 +01 +01 +ENDCHAR +STARTCHAR 05DB +ENCODING 1499 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 10 1 0 +BITMAP +F0 +FC +0C +06 +06 +06 +06 +0C +FC +F0 +ENDCHAR +STARTCHAR 05DC +ENCODING 1500 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 14 1 0 +BITMAP +80 +80 +80 +80 +FE +FE +06 +06 +04 +0C +18 +18 +18 +18 +ENDCHAR +STARTCHAR 05DD +ENCODING 1501 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +FE00 +FF00 +C380 +C180 +C180 +C180 +C180 +C180 +FF80 +FF80 +ENDCHAR +STARTCHAR 05DE +ENCODING 1502 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +CE00 +DF00 +7180 +6180 +6180 +6180 +4180 +4180 +CF80 +CF80 +ENDCHAR +STARTCHAR 05DF +ENCODING 1503 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 14 1 -4 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 05E0 +ENCODING 1504 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 4 10 1 0 +BITMAP +E0 +F0 +30 +10 +10 +10 +10 +10 +F0 +F0 +ENDCHAR +STARTCHAR 05E1 +ENCODING 1505 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3E00 +7F00 +E380 +C180 +C180 +C180 +C180 +E380 +7F00 +3E00 +ENDCHAR +STARTCHAR 05E2 +ENCODING 1506 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 11 0 -1 +BITMAP +6180 +2180 +3180 +3180 +3180 +3180 +1300 +1700 +1E00 +FC00 +E000 +ENDCHAR +STARTCHAR 05E3 +ENCODING 1507 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +FC +FF +C3 +C3 +C1 +C1 +71 +01 +01 +01 +01 +01 +01 +01 +ENDCHAR +STARTCHAR 05E4 +ENCODING 1508 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 1 0 +BITMAP +FC00 +FE00 +C700 +C300 +C180 +7180 +0300 +8700 +FE00 +7C00 +ENDCHAR +STARTCHAR 05E5 +ENCODING 1509 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 14 0 -4 +BITMAP +C3 +63 +23 +33 +16 +1C +18 +18 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR 05E6 +ENCODING 1510 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 10 0 0 +BITMAP +C3 +63 +33 +33 +1E +0C +04 +06 +7F +7F +ENDCHAR +STARTCHAR 05E7 +ENCODING 1511 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 -4 +BITMAP +FF +FF +01 +01 +43 +43 +46 +44 +44 +44 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 05E8 +ENCODING 1512 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 0 0 +BITMAP +FC +FE +07 +03 +01 +01 +01 +01 +01 +01 +ENDCHAR +STARTCHAR 05E9 +ENCODING 1513 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 10 1 0 +BITMAP +C460 +C460 +C460 +CC60 +CC60 +F860 +40C0 +61C0 +3F80 +1F00 +ENDCHAR +STARTCHAR 05EA +ENCODING 1514 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 10 0 0 +BITMAP +7F80 +7FC0 +18E0 +1860 +1860 +1860 +1860 +1860 +7860 +F060 +ENDCHAR +STARTCHAR 05F0 +ENCODING 1520 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 10 1 0 +BITMAP +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 05F1 +ENCODING 1521 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 10 1 0 +BITMAP +C6 +C6 +C6 +C6 +C6 +06 +06 +06 +06 +06 +ENDCHAR +STARTCHAR 05F2 +ENCODING 1522 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 5 1 5 +BITMAP +C6 +C6 +C6 +C6 +C6 +ENDCHAR +STARTCHAR 05F3 +ENCODING 1523 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 4 1 6 +BITMAP +40 +C0 +C0 +80 +ENDCHAR +STARTCHAR 05F4 +ENCODING 1524 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 6 4 1 6 +BITMAP +4C +C8 +C8 +98 +ENDCHAR +STARTCHAR 060C +ENCODING 1548 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 3 6 2 3 +BITMAP +40 +80 +80 +C0 +E0 +C0 +ENDCHAR +STARTCHAR 061B +ENCODING 1563 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 3 9 1 3 +BITMAP +20 +40 +80 +E0 +60 +60 +00 +60 +60 +ENDCHAR +STARTCHAR 061F +ENCODING 1567 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 5 11 1 3 +BITMAP +70 +C8 +98 +D8 +C0 +60 +20 +20 +00 +60 +60 +ENDCHAR +STARTCHAR 0621 +ENCODING 1569 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 6 8 1 1 +BITMAP +30 +70 +40 +CC +FC +70 +60 +40 +ENDCHAR +STARTCHAR 0622 +ENCODING 1570 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 14 0 2 +BITMAP +F0 +00 +00 +40 +60 +20 +20 +20 +20 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR 0623 +ENCODING 1571 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 14 1 2 +BITMAP +40 +80 +C0 +80 +00 +00 +80 +C0 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 0624 +ENCODING 1572 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 16 0 -2 +BITMAP +0C +10 +0C +10 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR 0625 +ENCODING 1573 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 16 0 -2 +BITMAP +40 +60 +20 +20 +20 +20 +20 +20 +20 +20 +20 +00 +60 +80 +60 +80 +ENDCHAR +STARTCHAR 0626 +ENCODING 1574 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 12 0 -1 +BITMAP +2000 +4000 +6060 +40F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +ENDCHAR +STARTCHAR 0627 +ENCODING 1575 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 11 1 3 +BITMAP +80 +C0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 0628 +ENCODING 1576 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 9 1 0 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 0629 +ENCODING 1577 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 11 0 3 +BITMAP +10 +D0 +C0 +00 +20 +30 +78 +48 +48 +78 +70 +ENDCHAR +STARTCHAR 062A +ENCODING 1578 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 9 1 3 +BITMAP +0300 +0F00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR 062B +ENCODING 1579 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 10 1 3 +BITMAP +0400 +0700 +0F00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR 062C +ENCODING 1580 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4000 +8600 +8600 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 062D +ENCODING 1581 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4000 +8000 +8000 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 062E +ENCODING 1582 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 16 0 -4 +BITMAP +0C00 +0400 +0000 +0000 +3F80 +7F80 +0C00 +3000 +4000 +8000 +8000 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 062F +ENCODING 1583 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 8 1 3 +BITMAP +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR 0630 +ENCODING 1584 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 12 1 3 +BITMAP +40 +60 +00 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR 0631 +ENCODING 1585 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 10 1 -2 +BITMAP +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR 0632 +ENCODING 1586 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 14 1 -2 +BITMAP +04 +04 +00 +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR 0633 +ENCODING 1587 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 9 1 -1 +BITMAP +0104 +0182 +0092 +80FE +80FE +8080 +8180 +FF00 +7C00 +ENDCHAR +STARTCHAR 0634 +ENCODING 1588 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 1 -1 +BITMAP +0030 +0038 +0028 +0020 +0000 +0104 +0182 +0092 +80FE +80FE +8080 +8180 +FF00 +7C00 +ENDCHAR +STARTCHAR 0635 +ENCODING 1589 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 9 1 -1 +BITMAP +0101E0 +0187F0 +008C30 +80FFF0 +80FFF0 +808000 +810000 +FF0000 +7C0000 +ENDCHAR +STARTCHAR 0636 +ENCODING 1590 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 12 1 -1 +BITMAP +000800 +000800 +000000 +0101E0 +0187F0 +008C30 +80FFF0 +80FFF0 +808000 +810000 +FF0000 +7C0000 +ENDCHAR +STARTCHAR 0637 +ENCODING 1591 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1800 +0800 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 0638 +ENCODING 1592 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1880 +0880 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 0639 +ENCODING 1593 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 16 0 -4 +BITMAP +3800 +7C00 +6000 +8000 +8100 +6700 +7C00 +3000 +2000 +4000 +8000 +8000 +C000 +6000 +7F80 +1F00 +ENDCHAR +STARTCHAR 063A +ENCODING 1594 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 20 0 -4 +BITMAP +1000 +1000 +0000 +0000 +3800 +7C00 +6000 +8000 +8100 +6700 +7C00 +3000 +2000 +4000 +8000 +8000 +C000 +6000 +7F80 +1F00 +ENDCHAR +STARTCHAR 0640 +ENCODING 1600 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 5 2 -1 3 +BITMAP +F8 +F8 +ENDCHAR +STARTCHAR 0641 +ENCODING 1601 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 10 0 3 +BITMAP +0018 +0018 +0000 +0000 +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR 0642 +ENCODING 1602 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 1 -1 +BITMAP +0100 +0580 +0400 +0000 +0000 +0180 +03C0 +02C0 +83C0 +83C0 +8040 +80C0 +FF80 +7F00 +ENDCHAR +STARTCHAR 0643 +ENCODING 1603 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 12 0 3 +BITMAP +0040 +0060 +0060 +0240 +0420 +0720 +0120 +0120 +9E20 +C020 +7FE0 +7FE0 +ENDCHAR +STARTCHAR 0644 +ENCODING 1604 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 15 1 0 +BITMAP +0200 +0300 +0100 +0100 +0100 +0100 +0100 +0100 +8100 +8080 +8080 +8080 +8300 +FF00 +7E00 +ENDCHAR +STARTCHAR 0645 +ENCODING 1605 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 11 0 -4 +BITMAP +38 +3C +0C +3C +7C +C0 +80 +80 +40 +40 +40 +ENDCHAR +STARTCHAR 0646 +ENCODING 1606 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 12 1 -1 +BITMAP +0800 +0800 +0000 +0000 +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR 0647 +ENCODING 1607 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 7 1 3 +BITMAP +40 +E0 +F0 +90 +90 +F0 +E0 +ENDCHAR +STARTCHAR 0648 +ENCODING 1608 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 11 0 -2 +BITMAP +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR 0649 +ENCODING 1609 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 10 0 -1 +BITMAP +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +ENDCHAR +STARTCHAR 064A +ENCODING 1610 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 13 0 -4 +BITMAP +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +0300 +0B00 +0800 +ENDCHAR +STARTCHAR 064B +ENCODING 1611 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 4 1 12 +BITMAP +60 +80 +60 +80 +ENDCHAR +STARTCHAR 064C +ENCODING 1612 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 3 5 1 12 +BITMAP +20 +60 +A0 +E0 +C0 +ENDCHAR +STARTCHAR 064D +ENCODING 1613 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 4 1 -3 +BITMAP +60 +80 +60 +80 +ENDCHAR +STARTCHAR 064E +ENCODING 1614 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 2 1 12 +BITMAP +60 +80 +ENDCHAR +STARTCHAR 064F +ENCODING 1615 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 5 1 12 +BITMAP +60 +60 +60 +60 +80 +ENDCHAR +STARTCHAR 0650 +ENCODING 1616 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 2 1 0 +BITMAP +60 +80 +ENDCHAR +STARTCHAR 0651 +ENCODING 1617 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 3 1 12 +BITMAP +40 +60 +80 +ENDCHAR +STARTCHAR 0652 +ENCODING 1618 +SWIDTH 154 0 +DWIDTH 3 0 +BBX 2 3 1 12 +BITMAP +C0 +C0 +C0 +ENDCHAR +STARTCHAR 0653 +ENCODING 1619 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 2 0 13 +BITMAP +C0 +70 +ENDCHAR +STARTCHAR 0654 +ENCODING 1620 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 4 1 13 +BITMAP +40 +80 +C0 +80 +ENDCHAR +STARTCHAR 0655 +ENCODING 1621 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 4 1 -3 +BITMAP +40 +80 +C0 +80 +ENDCHAR +STARTCHAR 0660 +ENCODING 1632 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 3 4 4 4 +BITMAP +80 +E0 +C0 +40 +ENDCHAR +STARTCHAR 0661 +ENCODING 1633 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 3 10 3 3 +BITMAP +80 +C0 +C0 +60 +60 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR 0662 +ENCODING 1634 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 5 11 2 3 +BITMAP +80 +C8 +F8 +78 +60 +20 +30 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 0663 +ENCODING 1635 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 4 +BITMAP +CD +7F +76 +20 +20 +10 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 0664 +ENCODING 1636 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 4 11 3 3 +BITMAP +10 +20 +40 +80 +F0 +60 +40 +40 +C0 +F0 +70 +ENDCHAR +STARTCHAR 0665 +ENCODING 1637 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 6 8 2 5 +BITMAP +30 +78 +9C +84 +84 +84 +F8 +F0 +ENDCHAR +STARTCHAR 0666 +ENCODING 1638 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 6 11 2 3 +BITMAP +80 +F0 +F0 +10 +10 +10 +10 +18 +08 +0C +04 +ENDCHAR +STARTCHAR 0667 +ENCODING 1639 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 3 +BITMAP +81 +C1 +C3 +62 +24 +24 +18 +18 +18 +08 +ENDCHAR +STARTCHAR 0668 +ENCODING 1640 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 4 +BITMAP +10 +18 +28 +28 +24 +26 +46 +C3 +81 +81 +ENDCHAR +STARTCHAR 0669 +ENCODING 1641 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 6 11 2 3 +BITMAP +30 +70 +98 +88 +F8 +78 +08 +08 +08 +0C +08 +ENDCHAR +STARTCHAR 066A +ENCODING 1642 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 13 1 2 +BITMAP +63 +62 +06 +04 +0C +08 +18 +10 +30 +20 +60 +46 +C6 +ENDCHAR +STARTCHAR 066B +ENCODING 1643 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 5 2 1 +BITMAP +C0 +C0 +C0 +40 +80 +ENDCHAR +STARTCHAR 066C +ENCODING 1644 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 3 6 2 3 +BITMAP +40 +80 +80 +C0 +E0 +C0 +ENDCHAR +STARTCHAR 066D +ENCODING 1645 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 6 7 2 7 +BITMAP +20 +E8 +50 +FC +70 +E8 +20 +ENDCHAR +STARTCHAR 066E +ENCODING 1646 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 6 1 3 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR 066F +ENCODING 1647 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 9 1 -1 +BITMAP +0180 +03C0 +02C0 +83C0 +83C0 +8040 +80C0 +FF80 +7F00 +ENDCHAR +STARTCHAR 0670 +ENCODING 1648 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 1 4 0 11 +BITMAP +80 +80 +80 +80 +ENDCHAR +STARTCHAR 0671 +ENCODING 1649 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 15 0 2 +BITMAP +30 +F0 +80 +00 +40 +60 +20 +20 +20 +20 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR 0672 +ENCODING 1650 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 15 0 2 +BITMAP +20 +40 +E0 +00 +40 +60 +20 +20 +20 +20 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR 0673 +ENCODING 1651 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 15 0 -1 +BITMAP +40 +60 +20 +20 +20 +20 +20 +20 +20 +20 +20 +00 +20 +40 +E0 +ENDCHAR +STARTCHAR 0674 +ENCODING 1652 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 4 1 13 +BITMAP +40 +80 +C0 +80 +ENDCHAR +STARTCHAR 0675 +ENCODING 1653 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 12 1 2 +BITMAP +10 +A0 +D0 +60 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 0676 +ENCODING 1654 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 9 16 0 -2 +BITMAP +0080 +0100 +0080 +0100 +0000 +0300 +0700 +0500 +0500 +0700 +0300 +0100 +0100 +0200 +8600 +7C00 +ENDCHAR +STARTCHAR 0677 +ENCODING 1655 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 9 16 0 -2 +BITMAP +0C80 +0D00 +0C80 +0D00 +1000 +0300 +0700 +0500 +0500 +0700 +0300 +0100 +0100 +0200 +8600 +7C00 +ENDCHAR +STARTCHAR 0678 +ENCODING 1656 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 15 0 -1 +BITMAP +0010 +0020 +0010 +0020 +0000 +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +ENDCHAR +STARTCHAR 0679 +ENCODING 1657 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 13 1 3 +BITMAP +0400 +0800 +0400 +0780 +0480 +1F80 +0000 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR 067A +ENCODING 1658 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 3 +BITMAP +0400 +0400 +0600 +0200 +0000 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR 067B +ENCODING 1659 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 -2 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0200 +0200 +0200 +0200 +ENDCHAR +STARTCHAR 067C +ENCODING 1660 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 1 +BITMAP +0300 +0F00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0500 +0600 +ENDCHAR +STARTCHAR 067D +ENCODING 1661 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 3 +BITMAP +0300 +0F00 +0E00 +0200 +0000 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR 067E +ENCODING 1662 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 -2 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0100 +0500 +0700 +0300 +ENDCHAR +STARTCHAR 067F +ENCODING 1663 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 3 +BITMAP +0200 +0A00 +0900 +0D00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR 0680 +ENCODING 1664 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 12 1 -3 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0300 +0D00 +0D00 +0500 +0400 +ENDCHAR +STARTCHAR 0681 +ENCODING 1665 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 0 -4 +BITMAP +0600 +0800 +0600 +0800 +0000 +3F80 +7F80 +0C00 +3000 +4000 +8000 +8000 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 0682 +ENCODING 1666 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 0 -4 +BITMAP +0C00 +0C00 +0400 +0400 +0000 +0000 +3F80 +7F80 +0C00 +3000 +4000 +8000 +8000 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 0683 +ENCODING 1667 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4000 +8300 +8B00 +8800 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 0684 +ENCODING 1668 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4000 +8400 +8400 +8600 +4600 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 0685 +ENCODING 1669 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 0 -4 +BITMAP +0800 +0A00 +0A00 +0800 +0000 +0000 +3F80 +7F80 +0C00 +3000 +4000 +8000 +8000 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 0686 +ENCODING 1670 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4200 +8B00 +8A00 +8200 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 0687 +ENCODING 1671 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4300 +8B00 +8D00 +8500 +4400 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 0688 +ENCODING 1672 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 15 0 3 +BITMAP +20 +20 +20 +2C +34 +FC +00 +10 +18 +0C +04 +04 +04 +7C +7C +ENDCHAR +STARTCHAR 0689 +ENCODING 1673 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 10 1 1 +BITMAP +20 +30 +18 +08 +08 +08 +F8 +F8 +50 +30 +ENDCHAR +STARTCHAR 068A +ENCODING 1674 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 11 1 0 +BITMAP +20 +30 +18 +08 +08 +08 +F8 +F8 +00 +20 +30 +ENDCHAR +STARTCHAR 068B +ENCODING 1675 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 18 0 0 +BITMAP +20 +20 +20 +2C +34 +FC +00 +10 +18 +0C +04 +04 +04 +7C +7C +00 +10 +18 +ENDCHAR +STARTCHAR 068C +ENCODING 1676 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 12 1 3 +BITMAP +30 +D0 +C0 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR 068D +ENCODING 1677 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 12 1 -1 +BITMAP +20 +30 +18 +08 +08 +08 +F8 +F8 +00 +10 +50 +40 +ENDCHAR +STARTCHAR 068E +ENCODING 1678 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 13 1 3 +BITMAP +40 +50 +50 +40 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR 068F +ENCODING 1679 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 13 1 3 +BITMAP +20 +A0 +A0 +20 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR 0690 +ENCODING 1680 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 14 1 3 +BITMAP +30 +B0 +90 +50 +40 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR 0691 +ENCODING 1681 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 17 1 -2 +BITMAP +08 +10 +08 +0F +09 +3F +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR 0692 +ENCODING 1682 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 13 1 -2 +BITMAP +0A +04 +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR 0693 +ENCODING 1683 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 11 1 -3 +BITMAP +02 +03 +01 +01 +01 +01 +01 +03 +8F +7D +03 +ENDCHAR +STARTCHAR 0694 +ENCODING 1684 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 11 1 -3 +BITMAP +02 +03 +01 +01 +01 +01 +01 +03 +8E +79 +01 +ENDCHAR +STARTCHAR 0695 +ENCODING 1685 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 12 1 -4 +BITMAP +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +05 +02 +ENDCHAR +STARTCHAR 0696 +ENCODING 1686 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 11 1 -3 +BITMAP +02 +03 +01 +01 +11 +11 +01 +03 +8E +79 +01 +ENDCHAR +STARTCHAR 0697 +ENCODING 1687 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 14 1 -2 +BITMAP +06 +1E +18 +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR 0698 +ENCODING 1688 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 15 1 -2 +BITMAP +0C +0F +0B +08 +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR 0699 +ENCODING 1689 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 16 1 -2 +BITMAP +04 +14 +16 +1A +18 +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR 069A +ENCODING 1690 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 12 1 -1 +BITMAP +0020 +0030 +0000 +0104 +0182 +0092 +80FE +80FE +8080 +8188 +FF08 +7C00 +ENDCHAR +STARTCHAR 069B +ENCODING 1691 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 10 1 -2 +BITMAP +0104 +0182 +0092 +80FE +80FE +8080 +8184 +FF14 +7C14 +0004 +ENDCHAR +STARTCHAR 069C +ENCODING 1692 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 15 1 -2 +BITMAP +0060 +0070 +0050 +0040 +0000 +0104 +0182 +0092 +80FE +80FE +8080 +8184 +FF14 +7C14 +0004 +ENDCHAR +STARTCHAR 069D +ENCODING 1693 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 9 1 -1 +BITMAP +0101E0 +0187F0 +008C30 +80FFF0 +80FFF0 +808000 +810100 +FF0500 +7C0400 +ENDCHAR +STARTCHAR 069E +ENCODING 1694 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 14 1 -1 +BITMAP +000400 +000500 +000500 +000400 +000000 +0101E0 +0187F0 +008C30 +80FFF0 +80FFF0 +808000 +810000 +FF0000 +7C0000 +ENDCHAR +STARTCHAR 069F +ENCODING 1695 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1880 +18E0 +09E0 +0980 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 06A0 +ENCODING 1696 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 21 0 -4 +BITMAP +2000 +2800 +2800 +2000 +0000 +3800 +7C00 +6000 +8000 +8100 +6700 +7C00 +3000 +2000 +4000 +8000 +8000 +C000 +6000 +7F80 +1F00 +ENDCHAR +STARTCHAR 06A1 +ENCODING 1697 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 6 0 3 +BITMAP +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR 06A2 +ENCODING 1698 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 9 0 0 +BITMAP +400C +800E +801A +C00E +7FFE +3FFE +0000 +0004 +0004 +ENDCHAR +STARTCHAR 06A3 +ENCODING 1699 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 12 0 0 +BITMAP +0008 +0008 +0000 +400C +800E +801A +C00E +7FFE +3FFE +0000 +0180 +0080 +ENDCHAR +STARTCHAR 06A4 +ENCODING 1700 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 11 0 3 +BITMAP +0010 +001C +0014 +0010 +0000 +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR 06A5 +ENCODING 1701 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 11 0 -2 +BITMAP +400C +800E +801A +C00E +7FFE +3FFE +0000 +0002 +000A +000A +0002 +ENDCHAR +STARTCHAR 06A6 +ENCODING 1702 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 12 0 3 +BITMAP +0004 +0014 +0014 +0014 +0010 +0000 +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR 06A7 +ENCODING 1703 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 13 1 -1 +BITMAP +0300 +0300 +0000 +0000 +0180 +03C0 +02C0 +83C0 +83C0 +8040 +80C0 +FF80 +7F00 +ENDCHAR +STARTCHAR 06A8 +ENCODING 1704 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 1 -1 +BITMAP +0400 +0700 +0500 +0400 +0000 +0180 +03C0 +02C0 +83C0 +83C0 +8040 +80C0 +FF80 +7F00 +ENDCHAR +STARTCHAR 06A9 +ENCODING 1705 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 11 0 3 +BITMAP +0006 +001E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +ENDCHAR +STARTCHAR 06AA +ENCODING 1706 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 10 0 3 +BITMAP +0080 +0380 +0E00 +1000 +1F00 +8FF8 +80FE +C002 +FFFE +7FFE +ENDCHAR +STARTCHAR 06AB +ENCODING 1707 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 11 0 3 +BITMAP +0006 +001E +007A +004C +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +ENDCHAR +STARTCHAR 06AC +ENCODING 1708 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 12 0 3 +BITMAP +0640 +0660 +0060 +0240 +0420 +0720 +0120 +0120 +9E20 +C020 +7FE0 +7FE0 +ENDCHAR +STARTCHAR 06AD +ENCODING 1709 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 13 0 3 +BITMAP +0C00 +0E40 +0B60 +0860 +0240 +0420 +0720 +0120 +0120 +9E20 +C020 +7FE0 +7FE0 +ENDCHAR +STARTCHAR 06AE +ENCODING 1710 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 17 0 -2 +BITMAP +0040 +0060 +0060 +0240 +0420 +0720 +0120 +0120 +9E20 +C020 +7FE0 +7FE0 +0000 +0300 +0F00 +0E00 +0200 +ENDCHAR +STARTCHAR 06AF +ENCODING 1711 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 14 0 3 +BITMAP +0002 +000E +0038 +0066 +009E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +ENDCHAR +STARTCHAR 06B0 +ENCODING 1712 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 14 0 3 +BITMAP +0002 +000E +0038 +0066 +009E +007A +004C +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +ENDCHAR +STARTCHAR 06B1 +ENCODING 1713 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 14 0 3 +BITMAP +0012 +004E +0038 +0066 +009E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +ENDCHAR +STARTCHAR 06B2 +ENCODING 1714 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 18 0 -1 +BITMAP +0002 +000E +0038 +0066 +009E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +0000 +0040 +0140 +0100 +ENDCHAR +STARTCHAR 06B3 +ENCODING 1715 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 19 0 -2 +BITMAP +0002 +000E +0038 +0066 +009E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +0000 +0080 +0080 +0080 +0080 +ENDCHAR +STARTCHAR 06B4 +ENCODING 1716 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 14 0 3 +BITMAP +0042 +001E +0078 +0066 +009E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +ENDCHAR +STARTCHAR 06B5 +ENCODING 1717 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 17 1 0 +BITMAP +0500 +0200 +0000 +0300 +0100 +0100 +0100 +0100 +0100 +0100 +8100 +8080 +8080 +8080 +8300 +FF00 +7E00 +ENDCHAR +STARTCHAR 06B6 +ENCODING 1718 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 17 1 0 +BITMAP +0300 +0300 +0000 +0300 +0100 +0100 +0100 +0100 +0100 +0100 +8100 +8080 +8080 +8080 +8300 +FF00 +7E00 +ENDCHAR +STARTCHAR 06B7 +ENCODING 1719 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 17 1 0 +BITMAP +0600 +0100 +0400 +0300 +0100 +0100 +0100 +0100 +0100 +0100 +8100 +8080 +8080 +8080 +8300 +FF00 +7E00 +ENDCHAR +STARTCHAR 06B8 +ENCODING 1720 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 19 1 -4 +BITMAP +0200 +0300 +0100 +0100 +0100 +0100 +0100 +0100 +8100 +8080 +8080 +8080 +8300 +FF00 +7E00 +0400 +2C00 +3C00 +0C00 +ENDCHAR +STARTCHAR 06B9 +ENCODING 1721 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 15 1 -4 +BITMAP +0800 +0800 +0000 +0000 +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +0000 +0800 +0800 +ENDCHAR +STARTCHAR 06BA +ENCODING 1722 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 8 1 0 +BITMAP +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR 06BB +ENCODING 1723 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 16 1 -1 +BITMAP +1000 +1000 +1000 +1600 +1A00 +3E00 +0000 +0000 +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR 06BC +ENCODING 1724 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 1 -3 +BITMAP +0800 +0800 +0000 +0000 +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +2800 +1800 +ENDCHAR +STARTCHAR 06BD +ENCODING 1725 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 1 -1 +BITMAP +1000 +1400 +3400 +1000 +0000 +0000 +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR 06BE +ENCODING 1726 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 8 -1 3 +BITMAP +08 +1C +1C +26 +27 +19 +FF +E7 +ENDCHAR +STARTCHAR 06BF +ENCODING 1727 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 16 0 -4 +BITMAP +0C00 +0400 +0000 +0000 +3F80 +7F80 +0C00 +3000 +4200 +8B00 +8A00 +8200 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR 06C0 +ENCODING 1728 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 12 1 3 +BITMAP +60 +80 +60 +80 +00 +40 +E0 +F0 +90 +90 +F0 +E0 +ENDCHAR +STARTCHAR 06C1 +ENCODING 1729 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 5 0 2 +BITMAP +18 +3C +6E +46 +80 +ENDCHAR +STARTCHAR 06C2 +ENCODING 1730 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 10 0 2 +BITMAP +10 +20 +10 +20 +00 +18 +3C +6E +46 +80 +ENDCHAR +STARTCHAR 06C3 +ENCODING 1731 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 9 0 2 +BITMAP +08 +28 +20 +00 +18 +3C +6E +46 +80 +ENDCHAR +STARTCHAR 06C4 +ENCODING 1732 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 11 0 -2 +BITMAP +03 +07 +05 +05 +07 +03 +01 +0D +16 +8E +7C +ENDCHAR +STARTCHAR 06C5 +ENCODING 1733 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 11 0 -2 +BITMAP +03 +07 +05 +05 +07 +03 +19 +07 +02 +86 +7C +ENDCHAR +STARTCHAR 06C6 +ENCODING 1734 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +05 +02 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR 06C7 +ENCODING 1735 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 17 0 -2 +BITMAP +03 +03 +03 +03 +04 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR 06C8 +ENCODING 1736 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 17 0 -1 +BITMAP +02 +02 +02 +02 +02 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR 06C9 +ENCODING 1737 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +02 +05 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR 06CA +ENCODING 1738 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 15 0 -2 +BITMAP +03 +0F +0C +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR 06CB +ENCODING 1739 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 16 0 -2 +BITMAP +04 +07 +0D +0C +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR 06CC +ENCODING 1740 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 10 0 -1 +BITMAP +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +ENDCHAR +STARTCHAR 06CD +ENCODING 1741 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 13 10 -1 -1 +BITMAP +0030 +0078 +0080 +2100 +E1F0 +40F0 +4010 +60E0 +3FC0 +1F00 +ENDCHAR +STARTCHAR 06CE +ENCODING 1742 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 11 0 -1 +BITMAP +0C00 +0860 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +ENDCHAR +STARTCHAR 06CF +ENCODING 1743 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +04 +04 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR 06D0 +ENCODING 1744 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 13 0 -4 +BITMAP +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +0000 +0400 +0400 +ENDCHAR +STARTCHAR 06D1 +ENCODING 1745 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 13 0 -4 +BITMAP +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +0000 +0A00 +0600 +ENDCHAR +STARTCHAR 06D2 +ENCODING 1746 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 14 7 1 0 +BITMAP +0600 +0E00 +3800 +7000 +8000 +FFFC +FFF8 +ENDCHAR +STARTCHAR 06D3 +ENCODING 1747 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 14 10 1 0 +BITMAP +0800 +3000 +1800 +2600 +0E00 +3800 +7000 +8000 +FFFC +FFF8 +ENDCHAR +STARTCHAR 06D4 +ENCODING 1748 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 2 0 3 +BITMAP +F0 +F0 +ENDCHAR +STARTCHAR 06D5 +ENCODING 1749 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 7 1 3 +BITMAP +40 +E0 +F0 +90 +90 +F0 +E0 +ENDCHAR +STARTCHAR 06D6 +ENCODING 1750 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 8 7 -4 10 +BITMAP +20 +20 +20 +23 +7F +80 +FE +ENDCHAR +STARTCHAR 06D7 +ENCODING 1751 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 7 7 -3 10 +BITMAP +28 +30 +38 +24 +7C +80 +FE +ENDCHAR +STARTCHAR 06D8 +ENCODING 1752 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 3 -2 10 +BITMAP +20 +50 +F0 +ENDCHAR +STARTCHAR 06D9 +ENCODING 1753 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 6 -2 10 +BITMAP +90 +D0 +50 +20 +60 +60 +ENDCHAR +STARTCHAR 06DA +ENCODING 1754 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 5 -2 10 +BITMAP +F0 +80 +A0 +80 +F0 +ENDCHAR +STARTCHAR 06DB +ENCODING 1755 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -1 10 +BITMAP +80 +C0 +ENDCHAR +STARTCHAR 06DC +ENCODING 1756 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 8 5 -4 10 +BITMAP +08 +09 +87 +88 +70 +ENDCHAR +STARTCHAR 06DD +ENCODING 1757 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 21 21 0 -4 +BITMAP +00FC00 +0F0380 +13FE80 +1C01C0 +100060 +200020 +400010 +400010 +400008 +400008 +800008 +400008 +400008 +400010 +400010 +200020 +100060 +1C01C0 +13FE80 +0F0380 +00FC00 +ENDCHAR +STARTCHAR 06DE +ENCODING 1758 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 21 0 -4 +BITMAP +002000 +005000 +008800 +1FFF80 +120480 +140280 +180180 +100080 +307040 +5090A0 +909090 +5090A0 +307040 +100080 +180180 +140280 +120480 +1FFF80 +008800 +005000 +002000 +ENDCHAR +STARTCHAR 06DF +ENCODING 1759 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 1 -1 10 +BITMAP +C0 +ENDCHAR +STARTCHAR 06E0 +ENCODING 1760 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 1 -1 10 +BITMAP +C0 +ENDCHAR +STARTCHAR 06E1 +ENCODING 1761 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 3 -3 10 +BITMAP +30 +18 +FC +ENDCHAR +STARTCHAR 06E2 +ENCODING 1762 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 5 -1 10 +BITMAP +40 +C0 +C0 +80 +80 +ENDCHAR +STARTCHAR 06E3 +ENCODING 1763 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 8 5 -4 -4 +BITMAP +08 +09 +87 +88 +70 +ENDCHAR +STARTCHAR 06E4 +ENCODING 1764 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 1 -1 10 +BITMAP +C0 +ENDCHAR +STARTCHAR 06E5 +ENCODING 1765 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 4 1 10 +BITMAP +60 +60 +40 +C0 +ENDCHAR +STARTCHAR 06E6 +ENCODING 1766 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 4 0 10 +BITMAP +10 +70 +80 +FE +ENDCHAR +STARTCHAR 06E7 +ENCODING 1767 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 7 4 -4 10 +BITMAP +10 +20 +C0 +FE +ENDCHAR +STARTCHAR 06E8 +ENCODING 1768 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 6 -2 10 +BITMAP +20 +00 +10 +90 +90 +F0 +ENDCHAR +STARTCHAR 06E9 +ENCODING 1769 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 16 1 0 +BITMAP +0800 +1400 +2A00 +5580 +AA40 +D780 +7700 +5300 +5300 +5300 +5300 +5F00 +6300 +9E80 +8080 +FFC0 +ENDCHAR +STARTCHAR 06EA +ENCODING 1770 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 3 -2 -3 +BITMAP +60 +90 +60 +ENDCHAR +STARTCHAR 06EB +ENCODING 1771 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 3 -2 10 +BITMAP +60 +90 +60 +ENDCHAR +STARTCHAR 06EC +ENCODING 1772 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 1 -1 10 +BITMAP +C0 +ENDCHAR +STARTCHAR 06ED +ENCODING 1773 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 5 -1 -4 +BITMAP +40 +C0 +C0 +80 +80 +ENDCHAR +STARTCHAR 06F0 +ENCODING 1776 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 3 4 4 4 +BITMAP +80 +E0 +C0 +40 +ENDCHAR +STARTCHAR 06F1 +ENCODING 1777 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 3 10 3 3 +BITMAP +80 +C0 +C0 +60 +60 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR 06F2 +ENCODING 1778 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 5 11 2 3 +BITMAP +80 +C8 +F8 +78 +60 +20 +30 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 06F3 +ENCODING 1779 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 4 +BITMAP +CD +7F +76 +20 +20 +10 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 06F4 +ENCODING 1780 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 7 10 2 3 +BITMAP +1C +A0 +E2 +FC +78 +60 +20 +20 +20 +20 +ENDCHAR +STARTCHAR 06F5 +ENCODING 1781 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 7 11 1 3 +BITMAP +10 +18 +1C +1C +26 +42 +42 +42 +CA +FE +7C +ENDCHAR +STARTCHAR 06F6 +ENCODING 1782 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 6 11 2 3 +BITMAP +30 +78 +80 +84 +FC +F8 +30 +20 +40 +40 +40 +ENDCHAR +STARTCHAR 06F7 +ENCODING 1783 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 3 +BITMAP +81 +C1 +C3 +62 +24 +24 +18 +18 +18 +08 +ENDCHAR +STARTCHAR 06F8 +ENCODING 1784 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 1 4 +BITMAP +10 +18 +28 +28 +24 +26 +46 +C3 +81 +81 +ENDCHAR +STARTCHAR 06F9 +ENCODING 1785 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 6 11 2 3 +BITMAP +30 +70 +98 +88 +F8 +78 +08 +08 +08 +0C +08 +ENDCHAR +STARTCHAR 06FA +ENCODING 1786 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 1 -1 +BITMAP +0060 +0070 +0050 +0040 +0000 +0104 +0182 +0092 +80FE +80FE +8080 +8188 +FF08 +7C00 +ENDCHAR +STARTCHAR 06FB +ENCODING 1787 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 12 1 -1 +BITMAP +000800 +000800 +000000 +0101E0 +0187F0 +008C30 +80FFF0 +80FFF0 +808000 +810200 +FF0200 +7C0000 +ENDCHAR +STARTCHAR 06FC +ENCODING 1788 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 20 0 -4 +BITMAP +1000 +1000 +0000 +0000 +3800 +7C00 +6000 +8000 +8100 +6700 +7C00 +3000 +2000 +4000 +8200 +8200 +C000 +6000 +7F80 +1F00 +ENDCHAR +STARTCHAR 06FD +ENCODING 1789 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 6 12 1 -3 +BITMAP +30 +70 +40 +CC +FC +70 +60 +40 +28 +28 +28 +28 +ENDCHAR +STARTCHAR 06FE +ENCODING 1790 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 11 0 -4 +BITMAP +38 +3C +0C +3C +7C +C0 +80 +94 +54 +54 +54 +ENDCHAR +STARTCHAR 1E80 +ENCODING 7808 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 18 18 0 0 +BITMAP +018000 +00C000 +004000 +000000 +C0C0C0 +40C080 +61E180 +61E180 +612180 +233100 +333300 +323300 +321200 +161A00 +161E00 +1C0E00 +1C0C00 +0C0C00 +ENDCHAR +STARTCHAR 1E81 +ENCODING 7809 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 14 0 0 +BITMAP +0600 +0600 +0200 +0000 +C308 +4308 +4718 +6798 +6590 +24B0 +3CB0 +3CE0 +18E0 +1860 +ENDCHAR +STARTCHAR 1E82 +ENCODING 7810 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 18 18 0 0 +BITMAP +006000 +004000 +00C000 +000000 +C0C0C0 +40C080 +61E180 +61E180 +612180 +233100 +333300 +323300 +321200 +161A00 +161E00 +1C0E00 +1C0C00 +0C0C00 +ENDCHAR +STARTCHAR 1E83 +ENCODING 7811 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 14 0 0 +BITMAP +0300 +0300 +0200 +0000 +C308 +4308 +4718 +6798 +6590 +24B0 +3CB0 +3CE0 +18E0 +1860 +ENDCHAR +STARTCHAR 1E84 +ENCODING 7812 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 18 17 0 0 +BITMAP +033000 +033000 +000000 +C0C0C0 +40C080 +61E180 +61E180 +612180 +233100 +333300 +323300 +321200 +161A00 +161E00 +1C0E00 +1C0C00 +0C0C00 +ENDCHAR +STARTCHAR 1E85 +ENCODING 7813 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 13 0 0 +BITMAP +0CC0 +0CC0 +0000 +C308 +4308 +4718 +6798 +6590 +24B0 +3CB0 +3CE0 +18E0 +1860 +ENDCHAR +STARTCHAR 1EA0 +ENCODING 7840 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 17 0 -3 +BITMAP +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1EA1 +ENCODING 7841 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 -3 +BITMAP +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 1EA2 +ENCODING 7842 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0F00 +0100 +0300 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 1EA3 +ENCODING 7843 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +1C00 +0600 +0C00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 1EA4 +ENCODING 7844 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0300 +0200 +0700 +0900 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 1EA5 +ENCODING 7845 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +1800 +3400 +2600 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 1EA6 +ENCODING 7846 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0C00 +0600 +0700 +0900 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 1EA7 +ENCODING 7847 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +3000 +1000 +1800 +0000 +1800 +3400 +2600 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 1EA8 +ENCODING 7848 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0700 +0300 +0700 +0900 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 1EA9 +ENCODING 7849 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +3C00 +0400 +0C00 +0000 +1800 +3400 +2600 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 1EAA +ENCODING 7850 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0C80 +1380 +0700 +0900 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 1EAB +ENCODING 7851 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 0 +BITMAP +3200 +4E00 +0000 +1800 +3400 +2600 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 1EAC +ENCODING 7852 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 20 0 -3 +BITMAP +0700 +0900 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1EAD +ENCODING 7853 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 -3 +BITMAP +1800 +3400 +2600 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 1EAE +ENCODING 7854 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0300 +0200 +0880 +0700 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 1EAF +ENCODING 7855 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +0C00 +0C00 +1800 +0000 +2200 +2200 +1C00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 1EB0 +ENCODING 7856 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0C00 +0600 +0880 +0700 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 1EB1 +ENCODING 7857 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +3000 +1800 +0800 +0000 +2200 +2200 +1C00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 1EB2 +ENCODING 7858 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0700 +0300 +0880 +0700 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 1EB3 +ENCODING 7859 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +1C00 +0600 +0C00 +0000 +2200 +2200 +1C00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 1EB4 +ENCODING 7860 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 18 0 0 +BITMAP +0E80 +1380 +0880 +0700 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +ENDCHAR +STARTCHAR 1EB5 +ENCODING 7861 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 0 +BITMAP +3B00 +2600 +0000 +2200 +2200 +1C00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +ENDCHAR +STARTCHAR 1EB6 +ENCODING 7862 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 20 0 -3 +BITMAP +0880 +0700 +0000 +0600 +0700 +0F00 +0D80 +0980 +1980 +18C0 +30C0 +3FE0 +3FE0 +6060 +6030 +4030 +C038 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1EB7 +ENCODING 7863 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 -3 +BITMAP +2200 +2200 +1C00 +0000 +3E00 +7F00 +C300 +0F00 +7F00 +F100 +C300 +C700 +FF00 +7D80 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 1EB8 +ENCODING 7864 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 17 1 -3 +BITMAP +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1EB9 +ENCODING 7865 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 -3 +BITMAP +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 1EBA +ENCODING 7866 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +0E00 +0300 +0600 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 1EBB +ENCODING 7867 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +1C00 +0600 +0C00 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EBC +ENCODING 7868 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +1C80 +1F80 +1300 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 1EBD +ENCODING 7869 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +3A00 +3E00 +6E00 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EBE +ENCODING 7870 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +0600 +0400 +0E00 +1B00 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 1EBF +ENCODING 7871 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EC0 +ENCODING 7872 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +0800 +0400 +0E00 +1B00 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 1EC1 +ENCODING 7873 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +3000 +1000 +1800 +0000 +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EC2 +ENCODING 7874 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +0E00 +0600 +0E00 +1B00 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 1EC3 +ENCODING 7875 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +3C00 +0400 +0C00 +0000 +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EC4 +ENCODING 7876 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 18 1 0 +BITMAP +1D00 +2700 +0E00 +1B00 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 1EC5 +ENCODING 7877 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 0 +BITMAP +3200 +4E00 +0000 +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EC6 +ENCODING 7878 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 20 1 -3 +BITMAP +0E00 +1B00 +0000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFC0 +FFC0 +C000 +C000 +C000 +C000 +FFE0 +FFE0 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1EC7 +ENCODING 7879 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 -3 +BITMAP +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +FF80 +FF80 +C000 +C380 +7F00 +3E00 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 1EC8 +ENCODING 7880 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 18 1 0 +BITMAP +E0 +30 +70 +00 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 1EC9 +ENCODING 7881 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 14 0 0 +BITMAP +F0 +10 +30 +00 +60 +60 +60 +60 +60 +60 +60 +60 +60 +60 +ENDCHAR +STARTCHAR 1ECA +ENCODING 7882 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 17 2 -3 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +00 +C0 +C0 +ENDCHAR +STARTCHAR 1ECB +ENCODING 7883 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 17 1 -3 +BITMAP +C0 +C0 +00 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +00 +C0 +C0 +ENDCHAR +STARTCHAR 1ECC +ENCODING 7884 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 17 1 -3 +BITMAP +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1ECD +ENCODING 7885 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 -3 +BITMAP +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 1ECE +ENCODING 7886 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0700 +0180 +0300 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 1ECF +ENCODING 7887 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +1C00 +0600 +0C00 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 1ED0 +ENCODING 7888 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0300 +0200 +0700 +0D80 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 1ED1 +ENCODING 7889 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 1ED2 +ENCODING 7890 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0400 +0200 +0700 +0D80 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 1ED3 +ENCODING 7891 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +3000 +1000 +1800 +0000 +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 1ED4 +ENCODING 7892 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0700 +0300 +0700 +0D80 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 1ED5 +ENCODING 7893 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 18 1 0 +BITMAP +3C00 +0400 +0C00 +0000 +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 1ED6 +ENCODING 7894 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 18 1 0 +BITMAP +0E40 +1380 +0700 +0D80 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 1ED7 +ENCODING 7895 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 0 +BITMAP +3200 +4E00 +0000 +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +ENDCHAR +STARTCHAR 1ED8 +ENCODING 7896 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 20 1 -3 +BITMAP +0700 +0D80 +0000 +0F80 +3FE0 +7070 +6030 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1ED9 +ENCODING 7897 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 -3 +BITMAP +1800 +3400 +2600 +0000 +3C00 +7F00 +C300 +C180 +8180 +8180 +C180 +C300 +7F00 +3C00 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 1EDA +ENCODING 7898 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 18 1 0 +BITMAP +0300 +0300 +0600 +0000 +0F86 +3FE6 +7072 +6034 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 1EDB +ENCODING 7899 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 1 0 +BITMAP +0C00 +0C00 +0800 +0000 +3C60 +7F60 +C320 +C1C0 +81C0 +8180 +C180 +C300 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EDC +ENCODING 7900 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 18 1 0 +BITMAP +0C00 +0600 +0200 +0000 +0F86 +3FE6 +7072 +6034 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 1EDD +ENCODING 7901 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 1 0 +BITMAP +3000 +1800 +0800 +0000 +3C60 +7F60 +C320 +C1C0 +81C0 +8180 +C180 +C300 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EDE +ENCODING 7902 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 18 1 0 +BITMAP +0700 +0180 +0300 +0000 +0F86 +3FE6 +7072 +6034 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 1EDF +ENCODING 7903 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 1 0 +BITMAP +1C00 +0600 +0C00 +0000 +3C60 +7F60 +C320 +C1C0 +81C0 +8180 +C180 +C300 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EE0 +ENCODING 7904 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 18 1 0 +BITMAP +0EC0 +0F80 +1B80 +0000 +0F86 +3FE6 +7072 +6034 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +ENDCHAR +STARTCHAR 1EE1 +ENCODING 7905 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 14 1 0 +BITMAP +3200 +7E00 +4E00 +0000 +3C60 +7F60 +C320 +C1C0 +81C0 +8180 +C180 +C300 +7F00 +3E00 +ENDCHAR +STARTCHAR 1EE2 +ENCODING 7906 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 17 1 -3 +BITMAP +0F86 +3FE6 +7072 +6034 +C018 +C018 +C018 +C018 +C018 +C018 +6030 +7070 +3FE0 +0F80 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1EE3 +ENCODING 7907 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 13 1 -3 +BITMAP +3C60 +7F60 +C320 +C1C0 +81C0 +8180 +C180 +C300 +7F00 +3E00 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 1EE4 +ENCODING 7908 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 17 1 -3 +BITMAP +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1EE5 +ENCODING 7909 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 13 1 -3 +BITMAP +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +00 +18 +18 +ENDCHAR +STARTCHAR 1EE6 +ENCODING 7910 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 18 1 0 +BITMAP +0E00 +0300 +0700 +0000 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 1EE7 +ENCODING 7911 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 0 +BITMAP +3C +04 +0C +00 +C1 +C1 +C1 +C1 +C1 +C1 +C3 +C3 +7D +39 +ENDCHAR +STARTCHAR 1EE8 +ENCODING 7912 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 18 1 0 +BITMAP +0300 +0600 +0400 +0000 +C064 +C064 +C064 +C064 +C078 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 1EE9 +ENCODING 7913 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +0C00 +0C00 +1800 +0000 +C160 +C160 +C120 +C120 +C1C0 +C100 +C300 +C300 +7D00 +3900 +ENDCHAR +STARTCHAR 1EEA +ENCODING 7914 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 18 1 0 +BITMAP +0C00 +0400 +0600 +0000 +C064 +C064 +C064 +C064 +C078 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 1EEB +ENCODING 7915 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +3000 +1800 +0800 +0000 +C160 +C160 +C120 +C120 +C1C0 +C100 +C300 +C300 +7D00 +3900 +ENDCHAR +STARTCHAR 1EEC +ENCODING 7916 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 18 1 0 +BITMAP +0E00 +0300 +0700 +0000 +C064 +C064 +C064 +C064 +C078 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 1EED +ENCODING 7917 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +3C00 +0400 +0C00 +0000 +C160 +C160 +C120 +C120 +C1C0 +C100 +C300 +C300 +7D00 +3900 +ENDCHAR +STARTCHAR 1EEE +ENCODING 7918 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 18 1 0 +BITMAP +0C80 +1F80 +1300 +0000 +C064 +C064 +C064 +C064 +C078 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 1EEF +ENCODING 7919 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 14 1 0 +BITMAP +3200 +7E00 +6E00 +0000 +C160 +C160 +C120 +C120 +C1C0 +C100 +C300 +C300 +7D00 +3900 +ENDCHAR +STARTCHAR 1EF0 +ENCODING 7920 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 17 1 -3 +BITMAP +C064 +C064 +C064 +C064 +C078 +C060 +C060 +C060 +4060 +4060 +6060 +70E0 +3FC0 +1F80 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1EF1 +ENCODING 7921 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 13 1 -3 +BITMAP +C160 +C160 +C120 +C120 +C1C0 +C100 +C300 +C300 +7D00 +3900 +0000 +1800 +1800 +ENDCHAR +STARTCHAR 1EF2 +ENCODING 7922 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 18 0 0 +BITMAP +0C00 +0600 +0200 +0000 +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 1EF3 +ENCODING 7923 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 18 1 -4 +BITMAP +30 +10 +18 +00 +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +30 +F0 +E0 +ENDCHAR +STARTCHAR 1EF4 +ENCODING 7924 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 17 0 -3 +BITMAP +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +0000 +0600 +0600 +ENDCHAR +STARTCHAR 1EF5 +ENCODING 7925 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 -4 +BITMAP +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +36 +F6 +E0 +ENDCHAR +STARTCHAR 1EF6 +ENCODING 7926 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 18 0 0 +BITMAP +0F00 +0100 +0300 +0000 +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 1EF7 +ENCODING 7927 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 18 1 -4 +BITMAP +38 +04 +1C +00 +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +30 +F0 +E0 +ENDCHAR +STARTCHAR 1EF8 +ENCODING 7928 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 12 18 0 0 +BITMAP +0EC0 +0F80 +1B80 +0000 +E030 +6070 +3060 +30C0 +1880 +0D80 +0F00 +0600 +0600 +0600 +0600 +0600 +0600 +0600 +ENDCHAR +STARTCHAR 1EF9 +ENCODING 7929 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 18 1 -4 +BITMAP +32 +7E +4C +00 +83 +C3 +C2 +46 +66 +64 +3C +3C +18 +18 +10 +30 +F0 +E0 +ENDCHAR +STARTCHAR 200C +ENCODING 8204 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 1 15 0 -3 +BITMAP +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR 200D +ENCODING 8205 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 16 -2 -2 +BITMAP +90 +E0 +D0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 200E +ENCODING 8206 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 15 0 -3 +BITMAP +20 +F0 +A0 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR 200F +ENCODING 8207 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 15 -4 -3 +BITMAP +40 +F0 +50 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 2013 +ENCODING 8211 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 2 0 4 +BITMAP +FFE0 +FFE0 +ENDCHAR +STARTCHAR 2014 +ENCODING 8212 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 19 2 0 4 +BITMAP +FFFFE0 +FFFFE0 +ENDCHAR +STARTCHAR 2015 +ENCODING 8213 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 17 2 1 4 +BITMAP +FFFF80 +FFFF80 +ENDCHAR +STARTCHAR 2017 +ENCODING 8215 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 5 0 -6 +BITMAP +FFE0 +FFE0 +0000 +FFE0 +FFE0 +ENDCHAR +STARTCHAR 2018 +ENCODING 8216 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 5 1 9 +BITMAP +40 +C0 +80 +C0 +C0 +ENDCHAR +STARTCHAR 2019 +ENCODING 8217 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 5 1 9 +BITMAP +C0 +C0 +40 +40 +80 +ENDCHAR +STARTCHAR 201A +ENCODING 8218 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 5 1 -3 +BITMAP +C0 +C0 +40 +40 +80 +ENDCHAR +STARTCHAR 201B +ENCODING 8219 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 5 1 9 +BITMAP +C0 +C0 +80 +C0 +40 +ENDCHAR +STARTCHAR 201C +ENCODING 8220 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 5 1 9 +BITMAP +50 +90 +90 +D0 +D0 +ENDCHAR +STARTCHAR 201D +ENCODING 8221 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 5 1 9 +BITMAP +D0 +D0 +50 +90 +90 +ENDCHAR +STARTCHAR 201E +ENCODING 8222 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 5 1 -3 +BITMAP +D0 +D0 +50 +90 +90 +ENDCHAR +STARTCHAR 2020 +ENCODING 8224 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 -3 +BITMAP +1800 +1800 +1800 +1800 +FF80 +FF80 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR 2021 +ENCODING 8225 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 17 1 -3 +BITMAP +1800 +1800 +1800 +1800 +FF80 +FF80 +1800 +1800 +1800 +1800 +1800 +FF80 +FF80 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR 2022 +ENCODING 8226 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 5 5 1 4 +BITMAP +70 +F8 +F8 +F8 +70 +ENDCHAR +STARTCHAR 2026 +ENCODING 8230 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 15 2 2 0 +BITMAP +C106 +C106 +ENDCHAR +STARTCHAR 202A +ENCODING 8234 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 14 0 -3 +BITMAP +F0 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR 202B +ENCODING 8235 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 14 -4 -3 +BITMAP +F0 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 202C +ENCODING 8236 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 16 -2 -3 +BITMAP +F0 +F0 +F0 +F0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 202D +ENCODING 8237 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 17 -2 -3 +BITMAP +F0 +80 +80 +80 +F0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 202E +ENCODING 8238 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 17 -2 -3 +BITMAP +F0 +10 +10 +10 +F0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 2030 +ENCODING 8240 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 19 14 0 0 +BITMAP +78C000 +488000 +CC8000 +CD0000 +CD0000 +4B0000 +7A0000 +02E3C0 +059240 +059660 +0D1660 +099660 +099240 +10E3C0 +ENDCHAR +STARTCHAR 2032 +ENCODING 8242 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 5 1 9 +BITMAP +60 +C0 +C0 +C0 +80 +ENDCHAR +STARTCHAR 2033 +ENCODING 8243 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 5 5 1 9 +BITMAP +78 +58 +D8 +D0 +90 +ENDCHAR +STARTCHAR 2039 +ENCODING 8249 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 9 1 1 +BITMAP +30 +20 +60 +40 +C0 +40 +60 +20 +30 +ENDCHAR +STARTCHAR 203A +ENCODING 8250 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 9 1 1 +BITMAP +C0 +40 +60 +20 +30 +20 +60 +40 +C0 +ENDCHAR +STARTCHAR 203C +ENCODING 8252 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 6 14 2 0 +BITMAP +CC +CC +CC +CC +CC +CC +CC +8C +8C +88 +88 +00 +CC +CC +ENDCHAR +STARTCHAR 203E +ENCODING 8254 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 7 2 0 11 +BITMAP +FE +FE +ENDCHAR +STARTCHAR 2044 +ENCODING 8260 +SWIDTH 154 0 +DWIDTH 3 0 +BBX 11 14 -4 0 +BITMAP +0020 +0040 +00C0 +0180 +0100 +0200 +0600 +0400 +0800 +1800 +3000 +2000 +4000 +C000 +ENDCHAR +STARTCHAR 206A +ENCODING 8298 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 17 -2 -3 +BITMAP +F0 +40 +40 +40 +F0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 206B +ENCODING 8299 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 17 -2 -3 +BITMAP +F0 +90 +90 +90 +F0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 206C +ENCODING 8300 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 16 -2 -3 +BITMAP +F0 +60 +60 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 206D +ENCODING 8301 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 16 -2 -3 +BITMAP +40 +60 +60 +F0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 206E +ENCODING 8302 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 17 -2 -3 +BITMAP +40 +60 +90 +60 +60 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 206F +ENCODING 8303 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 17 -2 -3 +BITMAP +C0 +40 +40 +40 +F0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR 207F +ENCODING 8319 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 5 7 1 5 +BITMAP +B0 +F8 +88 +88 +88 +88 +88 +ENDCHAR +STARTCHAR 20A3 +ENCODING 8355 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +3FC0 +3FC0 +2000 +2000 +2000 +2000 +3F80 +3F80 +2000 +2000 +FC00 +FC00 +2000 +2000 +ENDCHAR +STARTCHAR 20A4 +ENCODING 8356 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +1F00 +3F80 +3180 +60C0 +6000 +FE00 +FE00 +3000 +FE00 +FE00 +3000 +7C40 +FFC0 +4380 +ENDCHAR +STARTCHAR 20A7 +ENCODING 8359 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 14 0 0 +BITMAP +FE0000 +FF3000 +C3B000 +C1B000 +C1FFC0 +C3FFF0 +FFB430 +FE3700 +C037E0 +C031F0 +C03030 +C03630 +C03FF0 +C03BE0 +ENDCHAR +STARTCHAR 20AA +ENCODING 8362 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 13 10 1 0 +BITMAP +FF18 +FF98 +C098 +CC98 +CC98 +CC98 +CC98 +CC18 +CFF8 +CFF0 +ENDCHAR +STARTCHAR 20AB +ENCODING 8363 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 7 11 1 3 +BITMAP +0E +04 +3C +44 +44 +C4 +44 +44 +3C +00 +FE +ENDCHAR +STARTCHAR 20AC +ENCODING 8364 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 0 0 +BITMAP +0780 +1FC0 +3840 +3000 +6000 +6000 +FF80 +6000 +FF80 +6000 +3000 +3840 +1FC0 +0780 +ENDCHAR +STARTCHAR 2105 +ENCODING 8453 +SWIDTH 874 0 +DWIDTH 17 0 +BBX 15 14 1 0 +BITMAP +7820 +CC60 +8040 +80C0 +8480 +C980 +7900 +033C +0244 +0642 +04C2 +0842 +0844 +103C +ENDCHAR +STARTCHAR 2113 +ENCODING 8467 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 14 1 0 +BITMAP +30 +78 +48 +48 +50 +50 +60 +60 +C0 +C0 +40 +40 +78 +78 +ENDCHAR +STARTCHAR 2116 +ENCODING 8470 +SWIDTH 1028 0 +DWIDTH 20 0 +BBX 19 14 1 0 +BITMAP +E06000 +E06000 +F06000 +F06780 +D86FC0 +CC6C40 +CC6C60 +C66C40 +C76FC0 +C36780 +C1E000 +C1EFC0 +C0EFC0 +C06000 +ENDCHAR +STARTCHAR 2122 +ENCODING 8482 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 15 8 2 6 +BITMAP +FD86 +118E +114E +114E +1156 +1136 +1136 +1136 +ENDCHAR +STARTCHAR 2126 +ENCODING 8486 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 13 14 1 0 +BITMAP +0F80 +3FE0 +7060 +6030 +6030 +C030 +C010 +C010 +6030 +6030 +3060 +18C0 +FDF8 +FDF8 +ENDCHAR +STARTCHAR 212E +ENCODING 8494 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 10 1 0 +BITMAP +3E00 +6100 +C180 +C180 +FFC0 +C000 +C000 +C080 +6100 +3E00 +ENDCHAR +STARTCHAR 2153 +ENCODING 8531 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 1 0 +BITMAP +2010 +6030 +E020 +A040 +20C0 +2180 +2100 +023C +0664 +0C04 +0818 +1006 +3066 +603C +ENDCHAR +STARTCHAR 2154 +ENCODING 8532 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 0 0 +BITMAP +7808 +4C18 +0C10 +1820 +3060 +60C0 +FC80 +011E +0332 +0602 +040C +0802 +1822 +301E +ENDCHAR +STARTCHAR 215B +ENCODING 8539 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 14 1 0 +BITMAP +2010 +6030 +A020 +2040 +20C0 +2180 +2100 +0238 +0664 +0C64 +0838 +1044 +3044 +603C +ENDCHAR +STARTCHAR 215C +ENCODING 8540 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 0 0 +BITMAP +7808 +4C18 +0C10 +1020 +0C60 +CCC0 +7880 +011E +0332 +0632 +041C +0822 +1822 +301E +ENDCHAR +STARTCHAR 215D +ENCODING 8541 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 0 0 +BITMAP +7C08 +4018 +7810 +4C20 +0460 +CCC0 +7880 +011E +0332 +0632 +041C +0822 +1822 +301E +ENDCHAR +STARTCHAR 215E +ENCODING 8542 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 14 14 1 0 +BITMAP +F810 +1020 +3060 +2040 +6080 +4180 +4300 +0238 +0464 +0C64 +1838 +1044 +2044 +603C +ENDCHAR +STARTCHAR 2190 +ENCODING 8592 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 17 6 1 1 +BITMAP +100000 +200000 +600000 +FFFF80 +600000 +100000 +ENDCHAR +STARTCHAR 2191 +ENCODING 8593 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 7 16 1 -4 +BITMAP +10 +38 +7C +D2 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 2192 +ENCODING 8594 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 17 6 1 1 +BITMAP +000400 +000200 +000300 +FFFF80 +000200 +000400 +ENDCHAR +STARTCHAR 2193 +ENCODING 8595 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 7 16 1 -4 +BITMAP +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +90 +56 +3C +18 +10 +ENDCHAR +STARTCHAR 2194 +ENCODING 8596 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 17 7 1 1 +BITMAP +100400 +200200 +600300 +FFFF80 +600300 +100400 +100400 +ENDCHAR +STARTCHAR 2195 +ENCODING 8597 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 7 18 1 -4 +BITMAP +10 +18 +38 +74 +92 +10 +10 +10 +10 +10 +10 +10 +10 +90 +56 +3C +18 +10 +ENDCHAR +STARTCHAR 21A8 +ENCODING 8616 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 7 20 1 -6 +BITMAP +10 +18 +38 +74 +92 +10 +10 +10 +10 +10 +10 +10 +10 +90 +56 +3C +18 +10 +00 +FE +ENDCHAR +STARTCHAR 2202 +ENCODING 8706 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 14 1 0 +BITMAP +0E +1E +33 +21 +01 +0F +3D +61 +C3 +82 +86 +CC +FC +70 +ENDCHAR +STARTCHAR 2206 +ENCODING 8710 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 12 0 1 +BITMAP +0600 +0600 +0A00 +0B00 +1100 +1180 +2180 +20C0 +20C0 +4040 +4060 +FFE0 +ENDCHAR +STARTCHAR 220F +ENCODING 8719 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 13 18 1 -4 +BITMAP +FFF8 +FFF8 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +C018 +ENDCHAR +STARTCHAR 2211 +ENCODING 8721 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 18 1 -4 +BITMAP +FFF0 +FFF0 +6000 +3000 +3800 +1800 +0C00 +0600 +0300 +0700 +0600 +0C00 +1800 +3000 +6000 +6000 +FFF0 +FFF0 +ENDCHAR +STARTCHAR 2212 +ENCODING 8722 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 2 1 6 +BITMAP +FF80 +FF80 +ENDCHAR +STARTCHAR 2215 +ENCODING 8725 +SWIDTH 154 0 +DWIDTH 3 0 +BBX 11 14 -4 0 +BITMAP +0020 +0040 +00C0 +0180 +0100 +0200 +0600 +0400 +0800 +1800 +3000 +2000 +4000 +C000 +ENDCHAR +STARTCHAR 2219 +ENCODING 8729 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 2 2 6 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR 221A +ENCODING 8730 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 18 1 -1 +BITMAP +0080 +0080 +0080 +0080 +0080 +0080 +0100 +2100 +E100 +3100 +1900 +1900 +0E00 +0E00 +0600 +0600 +0200 +0200 +ENDCHAR +STARTCHAR 221E +ENCODING 8734 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 6 1 4 +BITMAP +71C0 +7BE0 +CE20 +CE20 +7BE0 +71C0 +ENDCHAR +STARTCHAR 221F +ENCODING 8735 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 12 12 3 0 +BITMAP +8000 +8000 +8000 +8000 +8000 +8000 +8000 +8000 +8000 +8000 +8000 +FFF0 +ENDCHAR +STARTCHAR 2229 +ENCODING 8745 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 14 1 0 +BITMAP +0F00 +3FC0 +70C0 +6060 +4020 +4020 +C020 +C020 +C020 +C020 +C020 +C020 +C020 +C020 +ENDCHAR +STARTCHAR 222B +ENCODING 8747 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 19 0 -2 +BITMAP +18 +38 +20 +20 +20 +20 +20 +20 +20 +20 +20 +20 +20 +20 +20 +20 +20 +E0 +C0 +ENDCHAR +STARTCHAR 2248 +ENCODING 8776 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 7 0 3 +BITMAP +7C40 +FFC0 +8780 +0000 +7C40 +FFC0 +8780 +ENDCHAR +STARTCHAR 2260 +ENCODING 8800 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 14 1 0 +BITMAP +0300 +0200 +0200 +0400 +FF80 +FF80 +0800 +1800 +FF80 +FF80 +2000 +2000 +4000 +4000 +ENDCHAR +STARTCHAR 2261 +ENCODING 8801 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 2 +BITMAP +FF80 +FF80 +0000 +0000 +FF80 +FF80 +0000 +0000 +FF80 +FF80 +ENDCHAR +STARTCHAR 2264 +ENCODING 8804 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 12 1 0 +BITMAP +0080 +0780 +1E00 +7000 +C000 +7000 +1E00 +0780 +0080 +0000 +FF80 +FF80 +ENDCHAR +STARTCHAR 2265 +ENCODING 8805 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 12 1 0 +BITMAP +8000 +E000 +7800 +0F00 +0180 +0F00 +7800 +E000 +8000 +0000 +FF80 +FF80 +ENDCHAR +STARTCHAR 2302 +ENCODING 8962 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 1 +BITMAP +0C00 +1200 +2100 +4100 +8080 +8080 +8080 +8080 +8080 +FF80 +ENDCHAR +STARTCHAR 2310 +ENCODING 8976 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 6 1 4 +BITMAP +FF80 +FF80 +C000 +C000 +C000 +C000 +ENDCHAR +STARTCHAR 2320 +ENCODING 8992 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 4 21 5 -5 +BITMAP +70 +10 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR 2321 +ENCODING 8993 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 4 21 2 -5 +BITMAP +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +10 +D0 +60 +ENDCHAR +STARTCHAR 2500 +ENCODING 9472 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 2 0 5 +BITMAP +FFFC +FFFC +ENDCHAR +STARTCHAR 2502 +ENCODING 9474 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 2 23 4 -6 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 250C +ENCODING 9484 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 13 6 -6 +BITMAP +FF +FF +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 2510 +ENCODING 9488 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 13 0 -6 +BITMAP +FF +FF +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +ENDCHAR +STARTCHAR 2514 +ENCODING 9492 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 12 6 5 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FF +FF +ENDCHAR +STARTCHAR 2518 +ENCODING 9496 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 12 0 5 +BITMAP +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +FF +FF +ENDCHAR +STARTCHAR 251C +ENCODING 9500 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 23 6 -6 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FF +FF +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 2524 +ENCODING 9508 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 23 0 -6 +BITMAP +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +FF +FF +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +03 +ENDCHAR +STARTCHAR 252C +ENCODING 9516 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 13 0 -6 +BITMAP +FFFC +FFFC +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 2534 +ENCODING 9524 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 12 0 5 +BITMAP +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +FFFC +FFFC +ENDCHAR +STARTCHAR 253C +ENCODING 9532 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 23 0 -6 +BITMAP +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +FFFC +FFFC +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 2550 +ENCODING 9552 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 5 0 3 +BITMAP +FFFC +0000 +0000 +FFFC +FFFC +ENDCHAR +STARTCHAR 2551 +ENCODING 9553 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 5 23 4 -6 +BITMAP +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +C8 +ENDCHAR +STARTCHAR 2552 +ENCODING 9554 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 14 6 -6 +BITMAP +FF +C0 +C0 +FF +FF +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 2553 +ENCODING 9555 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 10 13 4 -6 +BITMAP +FFC0 +FFC0 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +ENDCHAR +STARTCHAR 2554 +ENCODING 9556 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 10 14 4 -6 +BITMAP +FFC0 +C000 +C000 +CFC0 +CFC0 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +ENDCHAR +STARTCHAR 2555 +ENCODING 9557 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 14 0 -6 +BITMAP +FF +03 +03 +FF +FF +03 +03 +03 +03 +03 +03 +03 +03 +03 +ENDCHAR +STARTCHAR 2556 +ENCODING 9558 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 9 13 0 -6 +BITMAP +FF80 +FF80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +ENDCHAR +STARTCHAR 2557 +ENCODING 9559 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 9 14 0 -6 +BITMAP +FF80 +0080 +0080 +FC80 +FC80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +ENDCHAR +STARTCHAR 2558 +ENCODING 9560 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 14 6 3 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FF +C0 +C0 +FF +FF +ENDCHAR +STARTCHAR 2559 +ENCODING 9561 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 10 12 4 5 +BITMAP +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 255A +ENCODING 9562 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 10 14 4 3 +BITMAP +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +CFC0 +C000 +C000 +FFC0 +FFC0 +ENDCHAR +STARTCHAR 255B +ENCODING 9563 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 14 0 3 +BITMAP +03 +03 +03 +03 +03 +03 +03 +03 +03 +FF +03 +03 +FF +FF +ENDCHAR +STARTCHAR 255C +ENCODING 9564 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 9 12 0 5 +BITMAP +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +FF80 +FF80 +ENDCHAR +STARTCHAR 255D +ENCODING 9565 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 9 14 0 3 +BITMAP +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +FC80 +0080 +0080 +FF80 +FF80 +ENDCHAR +STARTCHAR 255E +ENCODING 9566 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 23 6 -6 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +FF +C0 +C0 +FF +FF +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR 255F +ENCODING 9567 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 10 23 4 -6 +BITMAP +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +CFC0 +CFC0 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +ENDCHAR +STARTCHAR 2560 +ENCODING 9568 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 10 23 4 -6 +BITMAP +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +CFC0 +C000 +C000 +CFC0 +CFC0 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +C800 +ENDCHAR +STARTCHAR 2561 +ENCODING 9569 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 8 23 0 -6 +BITMAP +03 +03 +03 +03 +03 +03 +03 +03 +03 +FF +03 +03 +FF +FF +03 +03 +03 +03 +03 +03 +03 +03 +03 +ENDCHAR +STARTCHAR 2562 +ENCODING 9570 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 9 23 0 -6 +BITMAP +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +FC80 +FC80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +ENDCHAR +STARTCHAR 2563 +ENCODING 9571 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 9 23 0 -6 +BITMAP +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +FC80 +0080 +0080 +FC80 +FC80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +ENDCHAR +STARTCHAR 2564 +ENCODING 9572 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 14 0 -6 +BITMAP +FFFC +0000 +0000 +FFFC +FFFC +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 2565 +ENCODING 9573 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 13 0 -6 +BITMAP +FFFC +FFFC +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +ENDCHAR +STARTCHAR 2566 +ENCODING 9574 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 14 0 -6 +BITMAP +FFFC +0000 +0000 +FCFC +FCFC +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +ENDCHAR +STARTCHAR 2567 +ENCODING 9575 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 14 0 3 +BITMAP +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +FFFC +0000 +0000 +FFFC +FFFC +ENDCHAR +STARTCHAR 2568 +ENCODING 9576 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 12 0 5 +BITMAP +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +FFFC +FFFC +ENDCHAR +STARTCHAR 2569 +ENCODING 9577 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 14 0 3 +BITMAP +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +FCFC +0000 +0000 +FFFC +FFFC +ENDCHAR +STARTCHAR 256A +ENCODING 9578 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 23 0 -6 +BITMAP +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +FFFC +0300 +0300 +FFFC +FFFC +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR 256B +ENCODING 9579 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 23 0 -6 +BITMAP +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +FFFC +FFFC +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +ENDCHAR +STARTCHAR 256C +ENCODING 9580 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 23 0 -6 +BITMAP +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +FCFC +0000 +0000 +FCFC +FCFC +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +0C80 +ENDCHAR +STARTCHAR 2580 +ENCODING 9600 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 11 0 6 +BITMAP +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +ENDCHAR +STARTCHAR 2584 +ENCODING 9604 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 12 0 -6 +BITMAP +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +ENDCHAR +STARTCHAR 2588 +ENCODING 9608 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 23 0 -6 +BITMAP +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +FFFC +ENDCHAR +STARTCHAR 258C +ENCODING 9612 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 7 23 0 -6 +BITMAP +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +ENDCHAR +STARTCHAR 2590 +ENCODING 9616 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 7 23 7 -6 +BITMAP +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +FE +ENDCHAR +STARTCHAR 2591 +ENCODING 9617 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 13 22 1 -5 +BITMAP +8440 +0000 +2118 +0000 +8440 +0000 +0000 +2118 +0000 +8440 +0000 +2118 +0000 +0000 +8440 +0000 +2118 +0000 +8440 +0000 +0000 +2118 +ENDCHAR +STARTCHAR 2592 +ENCODING 9618 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 14 22 0 -5 +BITMAP +52A4 +0000 +AD58 +0000 +52A4 +0000 +0000 +AD58 +0000 +52A4 +0000 +AD58 +0000 +0000 +52A4 +0000 +AD58 +0000 +52A4 +0000 +0000 +AD58 +ENDCHAR +STARTCHAR 2593 +ENCODING 9619 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 14 23 0 -6 +BITMAP +5AB4 +FFFC +A548 +FFFC +5AB4 +FFFC +FFFC +A548 +FFFC +5AB4 +FFFC +A548 +FFFC +FFFC +5AB4 +FFFC +A548 +FFFC +5AB4 +FFFC +FFFC +A548 +FFFC +ENDCHAR +STARTCHAR 25A0 +ENCODING 9632 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 9 1 0 +BITMAP +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +FF80 +ENDCHAR +STARTCHAR 25A1 +ENCODING 9633 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 9 1 0 +BITMAP +FF80 +8080 +8080 +8080 +8080 +8080 +8080 +8080 +FF80 +ENDCHAR +STARTCHAR 25AA +ENCODING 9642 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 5 4 1 4 +BITMAP +F8 +F8 +F8 +F8 +ENDCHAR +STARTCHAR 25AB +ENCODING 9643 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 5 4 1 4 +BITMAP +88 +88 +88 +F8 +ENDCHAR +STARTCHAR 25AC +ENCODING 9644 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 19 4 0 3 +BITMAP +FFFFE0 +FFFFE0 +FFFFE0 +FFFFE0 +ENDCHAR +STARTCHAR 25B2 +ENCODING 9650 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 13 13 3 0 +BITMAP +0200 +0200 +0700 +0700 +0F80 +0F80 +1FC0 +1FC0 +3FE0 +3FE0 +7FF0 +7FF0 +FFF8 +ENDCHAR +STARTCHAR 25BA +ENCODING 9658 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 13 13 3 0 +BITMAP +8000 +E000 +F800 +FE00 +FF80 +FFE0 +FFF8 +FFE0 +FF80 +FE00 +F800 +E000 +8000 +ENDCHAR +STARTCHAR 25BC +ENCODING 9660 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 13 13 3 0 +BITMAP +FFF8 +7FF0 +7FF0 +3FE0 +3FE0 +1FC0 +1FC0 +0F80 +0F80 +0700 +0700 +0200 +0200 +ENDCHAR +STARTCHAR 25C4 +ENCODING 9668 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 13 13 3 0 +BITMAP +0008 +0038 +00F8 +03F8 +0FF8 +3FF8 +FFF8 +3FF8 +0FF8 +03F8 +00F8 +0038 +0008 +ENDCHAR +STARTCHAR 25CA +ENCODING 9674 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 14 1 0 +BITMAP +10 +28 +28 +44 +42 +82 +81 +81 +82 +42 +44 +28 +28 +10 +ENDCHAR +STARTCHAR 25CB +ENCODING 9675 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 8 2 1 +BITMAP +44 +82 +81 +01 +01 +80 +42 +38 +ENDCHAR +STARTCHAR 25CF +ENCODING 9679 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 8 2 1 +BITMAP +7C +FE +FF +FF +FF +FE +7E +38 +ENDCHAR +STARTCHAR 25D8 +ENCODING 9688 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 9 1 0 +BITMAP +FF80 +FF80 +FF80 +E380 +E180 +E380 +FF80 +FF80 +FF80 +ENDCHAR +STARTCHAR 25D9 +ENCODING 9689 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 11 0 0 +BITMAP +FFE0 +FFE0 +E6E0 +DF60 +DFA0 +FFA0 +FFA0 +DFE0 +EF60 +F1E0 +FFE0 +ENDCHAR +STARTCHAR 25E6 +ENCODING 9702 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 5 5 1 4 +BITMAP +20 +90 +88 +88 +70 +ENDCHAR +STARTCHAR 263A +ENCODING 9786 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 12 12 4 -1 +BITMAP +0E00 +3080 +4040 +8020 +91A0 +9190 +8010 +8000 +9120 +4E20 +20C0 +1F00 +ENDCHAR +STARTCHAR 263B +ENCODING 9787 +SWIDTH 1028 0 +DWIDTH 20 0 +BBX 12 12 4 -1 +BITMAP +0F00 +3FC0 +7FE0 +7FE0 +EF70 +EF70 +FFF0 +FFF0 +7FE0 +70E0 +3FC0 +1F80 +ENDCHAR +STARTCHAR 263C +ENCODING 9788 +SWIDTH 874 0 +DWIDTH 17 0 +BBX 17 17 0 -2 +BITMAP +008000 +008000 +208200 +11E400 +0C1800 +080800 +000400 +100400 +F00780 +100400 +000400 +080800 +0C1800 +11E400 +208200 +008000 +008000 +ENDCHAR +STARTCHAR 2640 +ENCODING 9792 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 10 14 2 -4 +BITMAP +1E00 +0080 +0000 +8040 +8040 +8040 +0040 +4000 +2000 +0E00 +0000 +0000 +0000 +7FC0 +ENDCHAR +STARTCHAR 2642 +ENCODING 9794 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 12 17 1 -2 +BITMAP +0060 +0160 +0440 +0000 +0090 +0010 +0100 +0000 +2300 +4080 +8000 +8040 +8040 +8040 +8000 +4080 +2100 +ENDCHAR +STARTCHAR 2660 +ENCODING 9824 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 1 1 +BITMAP +1800 +3C00 +7E00 +FF00 +FF00 +FF80 +E700 +0000 +1800 +3E00 +ENDCHAR +STARTCHAR 2663 +ENCODING 9827 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 11 1 0 +BITMAP +1E00 +1F00 +1F00 +7EC0 +FFE0 +FFE0 +FFE0 +F3C0 +0400 +0C00 +3F00 +ENDCHAR +STARTCHAR 2665 +ENCODING 9829 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 11 1 0 +BITMAP +F780 +FFC0 +FFC0 +FF80 +7F80 +7F00 +3E00 +1E00 +1C00 +0800 +0800 +ENDCHAR +STARTCHAR 2666 +ENCODING 9830 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 11 1 0 +BITMAP +10 +18 +3C +7E +FE +FF +FE +7C +3C +18 +10 +ENDCHAR +STARTCHAR 266A +ENCODING 9834 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 13 0 0 +BITMAP +0600 +0780 +0380 +0080 +0080 +0000 +0000 +0000 +0000 +7800 +F800 +F800 +7800 +ENDCHAR +STARTCHAR 266B +ENCODING 9835 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 11 15 1 -1 +BITMAP +0060 +03E0 +0FE0 +0E20 +0820 +0820 +0820 +0820 +0820 +09E0 +0BE0 +FBE0 +F9E0 +F800 +F000 +ENDCHAR +STARTCHAR E801 +ENCODING 59393 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 12 -1 0 +BITMAP +C0 +00 +30 +30 +30 +30 +30 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR E802 +ENCODING 59394 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 0 -4 +BITMAP +FC +FE +07 +03 +01 +01 +01 +31 +01 +31 +01 +01 +01 +01 +ENDCHAR +STARTCHAR E803 +ENCODING 59395 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 0 -4 +BITMAP +FC +FE +07 +03 +01 +01 +01 +79 +11 +11 +01 +01 +01 +01 +ENDCHAR +STARTCHAR E804 +ENCODING 59396 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 10 14 -2 0 +BITMAP +1000 +D000 +1000 +1000 +1FC0 +1FC0 +00C0 +00C0 +0080 +0180 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR E805 +ENCODING 59397 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 10 14 -2 0 +BITMAP +1000 +D000 +1000 +1000 +1FC0 +1FC0 +00C0 +0CC0 +0080 +0180 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR E818 +ENCODING 59416 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 7 1 12 +BITMAP +60 +80 +60 +80 +40 +60 +80 +ENDCHAR +STARTCHAR E83A +ENCODING 59450 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 13 2 -1 3 +BITMAP +FFF8 +FFF8 +ENDCHAR +STARTCHAR F001 +ENCODING 61441 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 0 0 +BITMAP +1D +3D +30 +20 +F9 +F9 +21 +21 +21 +21 +21 +21 +21 +21 +ENDCHAR +STARTCHAR F002 +ENCODING 61442 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 0 0 +BITMAP +1D +3D +31 +21 +F9 +F9 +21 +21 +21 +21 +21 +21 +21 +21 +ENDCHAR +STARTCHAR F004 +ENCODING 61444 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 2 4 2 -5 +BITMAP +C0 +C0 +40 +80 +ENDCHAR +STARTCHAR F005 +ENCODING 61445 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 3 1 -4 +BITMAP +70 +30 +E0 +ENDCHAR +STARTCHAR F006 +ENCODING 61446 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 3 2 -4 16 +BITMAP +60 +C0 +ENDCHAR +STARTCHAR F007 +ENCODING 61447 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -7 16 +BITMAP +C0 +80 +ENDCHAR +STARTCHAR F008 +ENCODING 61448 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 3 3 -3 11 +BITMAP +60 +40 +C0 +ENDCHAR +STARTCHAR F009 +ENCODING 61449 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 3 3 -6 15 +BITMAP +60 +40 +C0 +ENDCHAR +STARTCHAR F00A +ENCODING 61450 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 1 21 0 -4 +BITMAP +80 +80 +80 +80 +80 +80 +80 +80 +80 +00 +00 +00 +80 +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR F00B +ENCODING 61451 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 21 -2 -4 +BITMAP +F0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +F0 +ENDCHAR +STARTCHAR F00C +ENCODING 61452 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 21 0 -4 +BITMAP +80 +C0 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR F00D +ENCODING 61453 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 21 -2 -4 +BITMAP +40 +C0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR F00E +ENCODING 61454 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 21 0 -4 +BITMAP +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +80 +C0 +ENDCHAR +STARTCHAR F00F +ENCODING 61455 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 3 3 -8 11 +BITMAP +C0 +60 +20 +ENDCHAR +STARTCHAR F010 +ENCODING 61456 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 2 -5 16 +BITMAP +F0 +30 +ENDCHAR +STARTCHAR F011 +ENCODING 61457 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 3 2 -8 16 +BITMAP +E0 +60 +ENDCHAR +STARTCHAR F012 +ENCODING 61458 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 3 -4 11 +BITMAP +E0 +30 +70 +ENDCHAR +STARTCHAR F013 +ENCODING 61459 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 3 -7 15 +BITMAP +E0 +30 +70 +ENDCHAR +STARTCHAR F014 +ENCODING 61460 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 4 3 -7 11 +BITMAP +E0 +30 +70 +ENDCHAR +STARTCHAR F015 +ENCODING 61461 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 7 2 -10 16 +BITMAP +72 +9C +ENDCHAR +STARTCHAR F016 +ENCODING 61462 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -10 15 +BITMAP +64 +9C +ENDCHAR +STARTCHAR F017 +ENCODING 61463 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -12 15 +BITMAP +64 +9C +ENDCHAR +STARTCHAR F018 +ENCODING 61464 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 7 2 -13 15 +BITMAP +76 +DC +ENDCHAR +STARTCHAR F019 +ENCODING 61465 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -8 11 +BITMAP +EC +98 +ENDCHAR +STARTCHAR F01A +ENCODING 61466 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -10 11 +BITMAP +E4 +98 +ENDCHAR +STARTCHAR F01B +ENCODING 61467 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -11 11 +BITMAP +64 +9C +ENDCHAR +STARTCHAR F01C +ENCODING 61468 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -6 15 +BITMAP +74 +DC +ENDCHAR +STARTCHAR F01D +ENCODING 61469 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -5 11 +BITMAP +E4 +98 +ENDCHAR +STARTCHAR F01E +ENCODING 61470 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -7 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F01F +ENCODING 61471 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -8 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F020 +ENCODING 61472 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -10 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F021 +ENCODING 61473 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -8 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F022 +ENCODING 61474 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 1 2 -10 -3 +BITMAP +80 +80 +ENDCHAR +STARTCHAR F023 +ENCODING 61475 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -6 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F024 +ENCODING 61476 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -6 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F025 +ENCODING 61477 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -6 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F026 +ENCODING 61478 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -8 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F027 +ENCODING 61479 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -7 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F028 +ENCODING 61480 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -9 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F029 +ENCODING 61481 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -4 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F02A +ENCODING 61482 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -3 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F02B +ENCODING 61483 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -7 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F02C +ENCODING 61484 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 2 2 -2 -3 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR F02D +ENCODING 61485 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -9 16 +BITMAP +E4 +98 +ENDCHAR +STARTCHAR F02E +ENCODING 61486 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -9 15 +BITMAP +E4 +98 +ENDCHAR +STARTCHAR F02F +ENCODING 61487 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -10 16 +BITMAP +E4 +98 +ENDCHAR +STARTCHAR F030 +ENCODING 61488 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -10 15 +BITMAP +EC +98 +ENDCHAR +STARTCHAR F031 +ENCODING 61489 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 10 1 0 +BITMAP +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR FB01 +ENCODING 64257 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 0 0 +BITMAP +1D +3D +30 +20 +F9 +F9 +21 +21 +21 +21 +21 +21 +21 +21 +ENDCHAR +STARTCHAR FB02 +ENCODING 64258 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 0 0 +BITMAP +1D +3D +31 +21 +F9 +F9 +21 +21 +21 +21 +21 +21 +21 +21 +ENDCHAR +STARTCHAR FB1D +ENCODING 64285 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 8 1 2 +BITMAP +C0 +C0 +C0 +C0 +C0 +00 +00 +40 +ENDCHAR +STARTCHAR FB1E +ENCODING 64286 +SWIDTH 0 0 +DWIDTH 0 0 +BBX 6 2 -3 12 +BITMAP +CC +78 +ENDCHAR +STARTCHAR FB1F +ENCODING 64287 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 7 1 3 +BITMAP +C6 +C6 +C6 +C6 +C6 +00 +FE +ENDCHAR +STARTCHAR FB20 +ENCODING 64288 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 0 0 +BITMAP +6180 +2180 +3180 +3180 +3180 +3180 +1300 +1B00 +FE00 +FC00 +ENDCHAR +STARTCHAR FB2A +ENCODING 64298 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 13 1 0 +BITMAP +0020 +0000 +0000 +C460 +C460 +C460 +CC60 +CC60 +F860 +40C0 +61C0 +3F80 +1F00 +ENDCHAR +STARTCHAR FB2B +ENCODING 64299 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 13 1 0 +BITMAP +C000 +0000 +0000 +C460 +C460 +C460 +CC60 +CC60 +F860 +40C0 +61C0 +3F80 +1F00 +ENDCHAR +STARTCHAR FB2C +ENCODING 64300 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 13 1 0 +BITMAP +0020 +0000 +0000 +C460 +C460 +C460 +CC60 +CC60 +FB60 +40C0 +61C0 +3F80 +1F00 +ENDCHAR +STARTCHAR FB2D +ENCODING 64301 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 13 1 0 +BITMAP +C000 +0000 +0000 +C460 +C460 +C460 +CC60 +CC60 +FB60 +40C0 +61C0 +3F80 +1F00 +ENDCHAR +STARTCHAR FB2E +ENCODING 64302 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 13 1 -3 +BITMAP +E180 +6100 +3300 +3B00 +7F00 +4E00 +C600 +C600 +C300 +C180 +0000 +0000 +3C00 +ENDCHAR +STARTCHAR FB2F +ENCODING 64303 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 15 1 -5 +BITMAP +E180 +6100 +3300 +3B00 +7F00 +4E00 +C600 +C600 +C300 +C180 +0000 +0000 +3C00 +0800 +0800 +ENDCHAR +STARTCHAR FB30 +ENCODING 64304 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +E180 +6100 +3300 +3B00 +7F00 +4E00 +C600 +C600 +DB00 +C180 +ENDCHAR +STARTCHAR FB31 +ENCODING 64305 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 10 0 0 +BITMAP +7C00 +7E00 +0700 +0300 +3100 +0100 +0100 +0100 +FFC0 +FFC0 +ENDCHAR +STARTCHAR FB32 +ENCODING 64306 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 7 10 0 0 +BITMAP +78 +7C +0C +3C +0C +1C +3C +64 +66 +C6 +ENDCHAR +STARTCHAR FB33 +ENCODING 64307 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 0 0 +BITMAP +FF80 +FF80 +0300 +1B00 +0300 +0300 +0300 +0300 +0300 +0300 +ENDCHAR +STARTCHAR FB34 +ENCODING 64308 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +FE00 +7F00 +0380 +0180 +C980 +C180 +C180 +C180 +C180 +C180 +ENDCHAR +STARTCHAR FB35 +ENCODING 64309 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 10 0 0 +BITMAP +30 +30 +30 +30 +F0 +30 +30 +30 +30 +30 +ENDCHAR +STARTCHAR FB36 +ENCODING 64310 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 10 0 0 +BITMAP +3F +3F +0C +D8 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR FB38 +ENCODING 64312 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +C700 +CF80 +C980 +C180 +CD80 +C180 +C180 +6380 +7F00 +1E00 +ENDCHAR +STARTCHAR FB39 +ENCODING 64313 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 5 0 5 +BITMAP +30 +30 +F0 +30 +30 +ENDCHAR +STARTCHAR FB3A +ENCODING 64314 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 0 -4 +BITMAP +FC +FE +07 +03 +11 +01 +01 +01 +01 +01 +01 +01 +01 +01 +ENDCHAR +STARTCHAR FB3B +ENCODING 64315 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 10 1 0 +BITMAP +F0 +FC +0C +06 +66 +06 +06 +0C +FC +F0 +ENDCHAR +STARTCHAR FB3C +ENCODING 64316 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 14 1 0 +BITMAP +80 +80 +80 +80 +FE +FE +06 +26 +04 +0C +18 +18 +18 +18 +ENDCHAR +STARTCHAR FB3E +ENCODING 64318 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +CE00 +DF00 +7180 +6180 +6D80 +6180 +4180 +4180 +CF80 +CF80 +ENDCHAR +STARTCHAR FB40 +ENCODING 64320 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 4 10 1 0 +BITMAP +E0 +F0 +30 +10 +90 +10 +10 +10 +F0 +F0 +ENDCHAR +STARTCHAR FB41 +ENCODING 64321 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 10 1 0 +BITMAP +3E00 +7F00 +E380 +C180 +C980 +C180 +C180 +E380 +7F00 +3E00 +ENDCHAR +STARTCHAR FB43 +ENCODING 64323 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 8 14 1 -4 +BITMAP +FC +FF +C3 +C3 +C1 +CD +61 +01 +01 +01 +01 +01 +01 +01 +ENDCHAR +STARTCHAR FB44 +ENCODING 64324 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 10 1 0 +BITMAP +FC00 +FE00 +C700 +C300 +C180 +6D80 +0300 +8700 +FE00 +7C00 +ENDCHAR +STARTCHAR FB46 +ENCODING 64326 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 10 0 0 +BITMAP +C3 +63 +33 +33 +7E +0C +04 +06 +7F +7F +ENDCHAR +STARTCHAR FB47 +ENCODING 64327 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 14 1 -4 +BITMAP +FF +FF +01 +19 +43 +43 +46 +44 +44 +44 +40 +40 +40 +40 +ENDCHAR +STARTCHAR FB48 +ENCODING 64328 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 8 10 0 0 +BITMAP +FC +FE +07 +03 +11 +01 +01 +01 +01 +01 +ENDCHAR +STARTCHAR FB49 +ENCODING 64329 +SWIDTH 668 0 +DWIDTH 13 0 +BBX 11 10 1 0 +BITMAP +C460 +C460 +C460 +CC60 +CC60 +FB60 +40C0 +61C0 +3F80 +1F00 +ENDCHAR +STARTCHAR FB4A +ENCODING 64330 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 10 0 0 +BITMAP +7F80 +7FC0 +18E0 +1860 +1B60 +1860 +1860 +1860 +7860 +F060 +ENDCHAR +STARTCHAR FB4B +ENCODING 64331 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 2 13 1 0 +BITMAP +40 +00 +00 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +ENDCHAR +STARTCHAR FB4C +ENCODING 64332 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 13 0 0 +BITMAP +1C00 +0000 +0000 +7C00 +7E00 +0700 +0300 +0100 +0100 +0100 +0100 +FFC0 +FFC0 +ENDCHAR +STARTCHAR FB4D +ENCODING 64333 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 7 13 1 0 +BITMAP +78 +00 +00 +F0 +FC +0C +06 +06 +06 +06 +0C +FC +F0 +ENDCHAR +STARTCHAR FB4E +ENCODING 64334 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 13 1 0 +BITMAP +3C00 +0000 +0000 +FC00 +FE00 +C700 +C300 +C180 +7180 +0300 +8700 +FE00 +7C00 +ENDCHAR +STARTCHAR FB4F +ENCODING 64335 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 14 1 0 +BITMAP +8000 +8000 +8000 +8000 +E180 +E180 +3180 +3B00 +7F00 +6E00 +4600 +4700 +C300 +C180 +ENDCHAR +STARTCHAR FB50 +ENCODING 64336 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 15 0 2 +BITMAP +30 +F0 +80 +00 +40 +60 +20 +20 +20 +20 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR FB51 +ENCODING 64337 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 5 14 -1 3 +BITMAP +30 +F0 +00 +00 +60 +20 +20 +20 +20 +20 +20 +20 +18 +18 +ENDCHAR +STARTCHAR FB52 +ENCODING 64338 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 -2 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0200 +0200 +0200 +0200 +ENDCHAR +STARTCHAR FB53 +ENCODING 64339 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 -2 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0200 +0200 +0200 +0200 +ENDCHAR +STARTCHAR FB54 +ENCODING 64340 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 -2 +BITMAP +08 +08 +0C +04 +FC +FC +00 +30 +30 +10 +10 +ENDCHAR +STARTCHAR FB55 +ENCODING 64341 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 -2 +BITMAP +08 +08 +0C +04 +FC +FC +00 +30 +30 +10 +10 +ENDCHAR +STARTCHAR FB56 +ENCODING 64342 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 -2 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0100 +0500 +0700 +0300 +ENDCHAR +STARTCHAR FB57 +ENCODING 64343 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 -2 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0100 +0500 +0700 +0300 +ENDCHAR +STARTCHAR FB58 +ENCODING 64344 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 -2 +BITMAP +08 +08 +0C +04 +FC +FC +00 +08 +28 +38 +18 +ENDCHAR +STARTCHAR FB59 +ENCODING 64345 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 -2 +BITMAP +08 +08 +0C +04 +FC +FC +00 +08 +28 +38 +18 +ENDCHAR +STARTCHAR FB5A +ENCODING 64346 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 12 1 -3 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0300 +0D00 +0D00 +0500 +0400 +ENDCHAR +STARTCHAR FB5B +ENCODING 64347 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 12 1 -3 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0300 +0D00 +0D00 +0500 +0400 +ENDCHAR +STARTCHAR FB5C +ENCODING 64348 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 12 -1 -3 +BITMAP +08 +08 +0C +04 +FC +FC +00 +18 +68 +68 +28 +20 +ENDCHAR +STARTCHAR FB5D +ENCODING 64349 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 12 -1 -3 +BITMAP +08 +08 +0C +04 +FC +FC +00 +18 +68 +68 +28 +20 +ENDCHAR +STARTCHAR FB5E +ENCODING 64350 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 3 +BITMAP +0400 +0400 +0600 +0200 +0000 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FB5F +ENCODING 64351 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 3 +BITMAP +0400 +0400 +0600 +0200 +0000 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FB60 +ENCODING 64352 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 3 +BITMAP +20 +20 +30 +10 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FB61 +ENCODING 64353 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 3 +BITMAP +20 +20 +30 +10 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FB62 +ENCODING 64354 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 3 +BITMAP +0200 +0A00 +0900 +0D00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FB63 +ENCODING 64355 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 11 1 3 +BITMAP +0200 +0A00 +0900 +0D00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FB64 +ENCODING 64356 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 3 +BITMAP +10 +50 +48 +68 +60 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FB65 +ENCODING 64357 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 3 +BITMAP +10 +50 +48 +68 +60 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FB66 +ENCODING 64358 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 13 1 3 +BITMAP +0400 +0800 +0400 +0780 +0480 +1F80 +0000 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FB67 +ENCODING 64359 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 13 1 3 +BITMAP +0400 +0800 +0400 +0780 +0480 +1F80 +0000 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FB68 +ENCODING 64360 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 13 -1 3 +BITMAP +20 +20 +20 +2C +34 +7C +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FB69 +ENCODING 64361 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 13 -1 3 +BITMAP +20 +20 +20 +2C +34 +7C +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FB6A +ENCODING 64362 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 11 0 3 +BITMAP +0010 +001C +0014 +0010 +0000 +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR FB6B +ENCODING 64363 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 11 0 3 +BITMAP +0010 +001C +0014 +0010 +0000 +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR FB6C +ENCODING 64364 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 12 -1 3 +BITMAP +20 +38 +78 +60 +18 +38 +2C +3C +3C +04 +FC +FC +ENDCHAR +STARTCHAR FB6D +ENCODING 64365 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 12 -1 3 +BITMAP +20 +28 +68 +20 +18 +1C +24 +24 +1C +0C +FC +FC +ENDCHAR +STARTCHAR FB6E +ENCODING 64366 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 12 0 3 +BITMAP +0004 +0014 +0014 +0014 +0010 +0000 +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR FB6F +ENCODING 64367 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 12 0 3 +BITMAP +0004 +0014 +0014 +0014 +0010 +0000 +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR FB70 +ENCODING 64368 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 14 -1 3 +BITMAP +10 +50 +48 +68 +60 +00 +18 +38 +2C +3C +3C +04 +FC +FC +ENDCHAR +STARTCHAR FB71 +ENCODING 64369 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 14 -1 3 +BITMAP +10 +50 +48 +68 +60 +00 +18 +1C +24 +24 +1C +0C +FC +FC +ENDCHAR +STARTCHAR FB72 +ENCODING 64370 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4000 +8400 +8400 +8600 +4600 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FB73 +ENCODING 64371 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 12 0 -4 +BITMAP +3F80 +7F80 +0E00 +31C0 +41C0 +8800 +8800 +8C00 +4C00 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FB74 +ENCODING 64372 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 11 -1 -2 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0400 +0400 +0600 +0600 +ENDCHAR +STARTCHAR FB75 +ENCODING 64373 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 11 -1 -2 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0400 +0400 +0600 +0600 +ENDCHAR +STARTCHAR FB76 +ENCODING 64374 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4000 +8300 +8B00 +8800 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FB77 +ENCODING 64375 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 12 0 -4 +BITMAP +3F80 +7F80 +0E00 +31C0 +41C0 +8400 +9400 +9000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FB78 +ENCODING 64376 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 10 -1 -1 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0200 +0A00 +0800 +ENDCHAR +STARTCHAR FB79 +ENCODING 64377 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 10 -1 -1 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0200 +0A00 +0800 +ENDCHAR +STARTCHAR FB7A +ENCODING 64378 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4200 +8B00 +8A00 +8200 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FB7B +ENCODING 64379 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 12 0 -4 +BITMAP +3F80 +7F80 +0E00 +31C0 +41C0 +8400 +9400 +9400 +4400 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FB7C +ENCODING 64380 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 11 -1 -2 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0200 +0A00 +0E00 +0200 +ENDCHAR +STARTCHAR FB7D +ENCODING 64381 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 11 -1 -2 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0200 +0A00 +0E00 +0200 +ENDCHAR +STARTCHAR FB7E +ENCODING 64382 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4300 +8B00 +8D00 +8500 +4400 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FB7F +ENCODING 64383 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 12 0 -4 +BITMAP +3F80 +7F80 +0E00 +31C0 +45C0 +9400 +9600 +9E00 +5800 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FB80 +ENCODING 64384 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 12 -1 -3 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0200 +0A00 +0B00 +0D00 +0C00 +ENDCHAR +STARTCHAR FB81 +ENCODING 64385 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 12 -1 -3 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0200 +0A00 +0B00 +0D00 +0C00 +ENDCHAR +STARTCHAR FB82 +ENCODING 64386 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 12 1 -1 +BITMAP +20 +30 +18 +08 +08 +08 +F8 +F8 +00 +10 +50 +40 +ENDCHAR +STARTCHAR FB83 +ENCODING 64387 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 12 1 -1 +BITMAP +20 +30 +18 +08 +08 +08 +F8 +F8 +00 +10 +50 +40 +ENDCHAR +STARTCHAR FB84 +ENCODING 64388 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 12 1 3 +BITMAP +30 +D0 +C0 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FB85 +ENCODING 64389 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 12 1 3 +BITMAP +30 +D0 +C0 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FB86 +ENCODING 64390 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 13 1 3 +BITMAP +40 +50 +50 +40 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FB87 +ENCODING 64391 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 13 1 3 +BITMAP +40 +50 +50 +40 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FB88 +ENCODING 64392 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 15 0 3 +BITMAP +20 +20 +20 +2C +34 +FC +00 +10 +18 +0C +04 +04 +04 +7C +7C +ENDCHAR +STARTCHAR FB89 +ENCODING 64393 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 15 0 3 +BITMAP +20 +20 +20 +2C +34 +FC +00 +10 +18 +0C +04 +04 +04 +7C +7C +ENDCHAR +STARTCHAR FB8A +ENCODING 64394 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 15 1 -2 +BITMAP +0C +0F +0B +08 +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR FB8B +ENCODING 64395 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 15 1 -2 +BITMAP +0C +0F +0B +08 +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR FB8C +ENCODING 64396 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 17 1 -2 +BITMAP +08 +10 +08 +0F +09 +3F +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR FB8D +ENCODING 64397 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 17 1 -2 +BITMAP +08 +10 +08 +0F +09 +3F +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR FB8E +ENCODING 64398 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 11 0 3 +BITMAP +0006 +001E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +ENDCHAR +STARTCHAR FB8F +ENCODING 64399 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 18 11 0 3 +BITMAP +000600 +001E00 +007800 +004000 +006000 +007000 +001800 +800C00 +801200 +FFF3C0 +7FE1C0 +ENDCHAR +STARTCHAR FB90 +ENCODING 64400 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 11 -1 3 +BITMAP +03 +0F +3C +20 +30 +38 +0C +02 +01 +FF +FF +ENDCHAR +STARTCHAR FB91 +ENCODING 64401 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 11 -1 3 +BITMAP +0300 +0F00 +3C00 +2000 +3000 +3800 +0C00 +0600 +0900 +F9E0 +F0E0 +ENDCHAR +STARTCHAR FB92 +ENCODING 64402 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 14 0 3 +BITMAP +0002 +000E +0038 +0066 +009E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +ENDCHAR +STARTCHAR FB93 +ENCODING 64403 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 18 14 0 3 +BITMAP +000200 +000E00 +003800 +006600 +009E00 +007800 +004000 +006000 +007000 +001800 +800C00 +801200 +FFF3C0 +7FE1C0 +ENDCHAR +STARTCHAR FB94 +ENCODING 64404 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 14 -1 3 +BITMAP +01 +07 +1C +33 +4F +3C +20 +30 +38 +0C +02 +01 +FF +FF +ENDCHAR +STARTCHAR FB95 +ENCODING 64405 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 14 -1 3 +BITMAP +0100 +0700 +1C00 +3300 +4F00 +3C00 +2000 +3000 +3800 +0C00 +0600 +0900 +F9E0 +F0E0 +ENDCHAR +STARTCHAR FB96 +ENCODING 64406 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 19 0 -2 +BITMAP +0002 +000E +0038 +0066 +009E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +0000 +0080 +0080 +0080 +0080 +ENDCHAR +STARTCHAR FB97 +ENCODING 64407 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 18 19 0 -2 +BITMAP +000200 +000E00 +003800 +006600 +009E00 +007800 +004000 +006000 +007000 +001800 +800C00 +801200 +FFF3C0 +7FE1C0 +000000 +020000 +020000 +020000 +020000 +ENDCHAR +STARTCHAR FB98 +ENCODING 64408 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 19 -1 -2 +BITMAP +01 +07 +1C +33 +4F +3C +20 +30 +38 +0C +02 +01 +FF +FF +00 +08 +08 +0C +0C +ENDCHAR +STARTCHAR FB99 +ENCODING 64409 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 19 -1 -2 +BITMAP +0100 +0700 +1C00 +3300 +4F00 +3C00 +2000 +3000 +3800 +0C00 +0600 +0900 +F9E0 +F0E0 +0000 +3000 +3000 +1000 +1000 +ENDCHAR +STARTCHAR FB9A +ENCODING 64410 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 14 0 3 +BITMAP +0012 +004E +0038 +0066 +009E +0078 +0040 +0060 +0070 +0018 +8004 +8002 +FFFE +7FFE +ENDCHAR +STARTCHAR FB9B +ENCODING 64411 +SWIDTH 925 0 +DWIDTH 18 0 +BBX 18 14 0 3 +BITMAP +001200 +004E00 +003800 +006600 +009E00 +007800 +004000 +006000 +007000 +001800 +800C00 +801200 +FFF3C0 +7FE1C0 +ENDCHAR +STARTCHAR FB9C +ENCODING 64412 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 14 -1 3 +BITMAP +09 +27 +1C +33 +4F +3C +20 +30 +38 +0C +02 +01 +FF +FF +ENDCHAR +STARTCHAR FB9D +ENCODING 64413 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 14 -1 3 +BITMAP +0900 +2700 +1C00 +3300 +4F00 +3C00 +2000 +3000 +3800 +0C00 +0600 +0900 +F9E0 +F0E0 +ENDCHAR +STARTCHAR FB9E +ENCODING 64414 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 8 1 0 +BITMAP +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR FB9F +ENCODING 64415 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 8 1 0 +BITMAP +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR FBA0 +ENCODING 64416 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 16 1 -1 +BITMAP +1000 +1000 +1000 +1600 +1A00 +3E00 +0000 +0000 +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR FBA1 +ENCODING 64417 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 16 1 -1 +BITMAP +1000 +1000 +1000 +1600 +1A00 +3E00 +0000 +0000 +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR FBA2 +ENCODING 64418 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 13 -1 3 +BITMAP +20 +20 +20 +2C +34 +7C +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FBA3 +ENCODING 64419 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 13 -1 3 +BITMAP +20 +20 +20 +2C +34 +7C +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FBA4 +ENCODING 64420 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 12 1 3 +BITMAP +60 +80 +60 +80 +00 +40 +E0 +F0 +90 +90 +F0 +E0 +ENDCHAR +STARTCHAR FBA5 +ENCODING 64421 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 13 0 3 +BITMAP +30 +40 +30 +40 +10 +10 +28 +48 +F8 +E8 +08 +06 +06 +ENDCHAR +STARTCHAR FBA6 +ENCODING 64422 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 7 1 3 +BITMAP +40 +E0 +F0 +90 +90 +F0 +E0 +ENDCHAR +STARTCHAR FBA7 +ENCODING 64423 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 5 0 2 +BITMAP +18 +3C +6E +46 +80 +ENDCHAR +STARTCHAR FBA8 +ENCODING 64424 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 -2 +BITMAP +08 +08 +0C +04 +FC +FC +00 +10 +20 +30 +10 +ENDCHAR +STARTCHAR FBA9 +ENCODING 64425 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 7 12 -1 -4 +BITMAP +10 +10 +10 +F6 +DE +18 +10 +00 +08 +10 +08 +08 +ENDCHAR +STARTCHAR FBAA +ENCODING 64426 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 8 -1 3 +BITMAP +08 +1C +1C +26 +27 +19 +FF +E7 +ENDCHAR +STARTCHAR FBAB +ENCODING 64427 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 9 0 4 +BITMAP +10 +10 +28 +48 +F8 +E8 +08 +06 +06 +ENDCHAR +STARTCHAR FBAC +ENCODING 64428 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 8 -1 3 +BITMAP +08 +1C +1C +26 +27 +19 +FF +E7 +ENDCHAR +STARTCHAR FBAD +ENCODING 64429 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 10 -1 -1 +BITMAP +04 +0C +14 +18 +FF +FF +24 +34 +1E +0C +ENDCHAR +STARTCHAR FBAE +ENCODING 64430 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 14 7 1 0 +BITMAP +0600 +0E00 +3800 +7000 +8000 +FFFC +FFF8 +ENDCHAR +STARTCHAR FBAF +ENCODING 64431 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 14 9 1 -4 +BITMAP +00FC +07FC +1F00 +3800 +6000 +8000 +8000 +FFFC +FFF8 +ENDCHAR +STARTCHAR FBB0 +ENCODING 64432 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 14 10 1 0 +BITMAP +0800 +3000 +1800 +2600 +0E00 +3800 +7000 +8000 +FFFC +FFF8 +ENDCHAR +STARTCHAR FBB1 +ENCODING 64433 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 14 14 1 -4 +BITMAP +0020 +0040 +0020 +0040 +0000 +00FC +07FC +1F00 +3800 +6000 +8000 +8000 +FFFC +FFF8 +ENDCHAR +STARTCHAR FBD3 +ENCODING 64467 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 13 0 3 +BITMAP +0C00 +0E40 +0B60 +0860 +0240 +0420 +0720 +0120 +0120 +9E20 +C020 +7FE0 +7FE0 +ENDCHAR +STARTCHAR FBD4 +ENCODING 64468 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 13 0 3 +BITMAP +0C00 +0E40 +0B60 +0860 +0240 +0420 +0720 +0120 +0120 +9E20 +C020 +7FE0 +7FE0 +ENDCHAR +STARTCHAR FBD5 +ENCODING 64469 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 13 -1 3 +BITMAP +30 +0C +23 +0F +3C +20 +30 +38 +0C +02 +01 +FF +FF +ENDCHAR +STARTCHAR FBD6 +ENCODING 64470 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 13 -1 3 +BITMAP +30 +0C +23 +0F +3C +20 +30 +38 +0C +02 +01 +FF +FF +ENDCHAR +STARTCHAR FBD7 +ENCODING 64471 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 17 0 -2 +BITMAP +03 +03 +03 +03 +04 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBD8 +ENCODING 64472 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 17 0 -2 +BITMAP +03 +03 +03 +03 +04 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBD9 +ENCODING 64473 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +05 +02 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBDA +ENCODING 64474 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +05 +02 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBDB +ENCODING 64475 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 17 0 -1 +BITMAP +02 +02 +02 +02 +02 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBDC +ENCODING 64476 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 17 0 -1 +BITMAP +02 +02 +02 +02 +02 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBDD +ENCODING 64477 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 9 16 0 -2 +BITMAP +0C80 +0D00 +0C80 +0D00 +1000 +0300 +0700 +0500 +0500 +0700 +0300 +0100 +0100 +0200 +8600 +7C00 +ENDCHAR +STARTCHAR FBDE +ENCODING 64478 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 16 0 -2 +BITMAP +04 +07 +0D +0C +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBDF +ENCODING 64479 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 16 0 -2 +BITMAP +04 +07 +0D +0C +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBE0 +ENCODING 64480 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 11 0 -2 +BITMAP +03 +07 +05 +05 +07 +03 +19 +07 +02 +86 +7C +ENDCHAR +STARTCHAR FBE1 +ENCODING 64481 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 11 0 -2 +BITMAP +03 +07 +05 +05 +07 +03 +19 +07 +02 +86 +7C +ENDCHAR +STARTCHAR FBE2 +ENCODING 64482 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +02 +05 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBE3 +ENCODING 64483 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 14 0 -2 +BITMAP +02 +05 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FBE4 +ENCODING 64484 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 13 0 -4 +BITMAP +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +0000 +0400 +0400 +ENDCHAR +STARTCHAR FBE5 +ENCODING 64485 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 9 0 -4 +BITMAP +01E0 +43E0 +8200 +83C0 +C040 +7FC0 +3F00 +0400 +0400 +ENDCHAR +STARTCHAR FBE6 +ENCODING 64486 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 -2 +BITMAP +08 +08 +0C +04 +FC +FC +00 +30 +30 +10 +10 +ENDCHAR +STARTCHAR FBE7 +ENCODING 64487 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 -2 +BITMAP +08 +08 +0C +04 +FC +FC +00 +30 +30 +10 +10 +ENDCHAR +STARTCHAR FBFC +ENCODING 64508 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 10 0 -1 +BITMAP +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +ENDCHAR +STARTCHAR FBFD +ENCODING 64509 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 7 0 -2 +BITMAP +01E0 +43E0 +8200 +83C0 +C040 +7FC0 +3F00 +ENDCHAR +STARTCHAR FBFE +ENCODING 64510 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 10 -1 -1 +BITMAP +08 +08 +0C +04 +FC +FC +00 +08 +28 +20 +ENDCHAR +STARTCHAR FBFF +ENCODING 64511 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 10 -1 -1 +BITMAP +08 +08 +0C +04 +FC +FC +00 +08 +28 +20 +ENDCHAR +STARTCHAR FC5E +ENCODING 64606 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 8 0 12 +BITMAP +10 +30 +50 +F0 +40 +20 +30 +40 +ENDCHAR +STARTCHAR FC5F +ENCODING 64607 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 7 1 12 +BITMAP +40 +60 +80 +60 +80 +60 +80 +ENDCHAR +STARTCHAR FC60 +ENCODING 64608 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 5 1 12 +BITMAP +60 +80 +40 +60 +80 +ENDCHAR +STARTCHAR FC61 +ENCODING 64609 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 7 1 12 +BITMAP +60 +60 +60 +80 +40 +60 +80 +ENDCHAR +STARTCHAR FC62 +ENCODING 64610 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 5 1 12 +BITMAP +40 +60 +80 +60 +80 +ENDCHAR +STARTCHAR FD3E +ENCODING 64830 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 19 1 -4 +BITMAP +10 +10 +20 +60 +60 +60 +C0 +C0 +C0 +C0 +C0 +C0 +C0 +40 +60 +60 +20 +10 +10 +ENDCHAR +STARTCHAR FD3F +ENCODING 64831 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 4 19 1 -4 +BITMAP +80 +80 +40 +60 +60 +20 +30 +30 +30 +30 +30 +30 +30 +20 +60 +60 +40 +80 +80 +ENDCHAR +STARTCHAR FDF2 +ENCODING 65010 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 11 13 0 3 +BITMAP +0200 +0000 +0080 +0580 +0600 +0100 +0120 +0920 +1920 +2920 +49A0 +FFE0 +E760 +ENDCHAR +STARTCHAR FE80 +ENCODING 65152 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 6 8 1 1 +BITMAP +30 +70 +40 +CC +FC +70 +60 +40 +ENDCHAR +STARTCHAR FE81 +ENCODING 65153 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 14 0 2 +BITMAP +F0 +00 +00 +40 +60 +20 +20 +20 +20 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR FE82 +ENCODING 65154 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 13 0 3 +BITMAP +E0 +00 +00 +C0 +40 +40 +40 +40 +40 +40 +40 +30 +30 +ENDCHAR +STARTCHAR FE83 +ENCODING 65155 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 14 1 2 +BITMAP +40 +80 +C0 +80 +00 +00 +80 +C0 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR FE84 +ENCODING 65156 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 13 0 3 +BITMAP +40 +80 +60 +80 +00 +40 +C0 +40 +40 +40 +40 +70 +30 +ENDCHAR +STARTCHAR FE85 +ENCODING 65157 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 16 0 -2 +BITMAP +0C +10 +0C +10 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FE86 +ENCODING 65158 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 16 0 -2 +BITMAP +0C +10 +0C +10 +00 +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FE87 +ENCODING 65159 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 3 16 0 -2 +BITMAP +40 +60 +20 +20 +20 +20 +20 +20 +20 +20 +20 +00 +60 +80 +60 +80 +ENDCHAR +STARTCHAR FE88 +ENCODING 65160 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 15 0 -1 +BITMAP +C0 +40 +40 +40 +40 +40 +40 +40 +30 +30 +00 +40 +80 +60 +80 +ENDCHAR +STARTCHAR FE89 +ENCODING 65161 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 12 0 -1 +BITMAP +2000 +4000 +6060 +40F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +ENDCHAR +STARTCHAR FE8A +ENCODING 65162 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 13 0 -2 +BITMAP +2000 +4000 +6000 +4000 +0000 +0000 +01E0 +43E0 +8200 +83C0 +C040 +7FC0 +3F00 +ENDCHAR +STARTCHAR FE8B +ENCODING 65163 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 3 +BITMAP +10 +20 +10 +20 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FE8C +ENCODING 65164 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 3 +BITMAP +10 +20 +10 +20 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FE8D +ENCODING 65165 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 2 11 1 3 +BITMAP +80 +C0 +40 +40 +40 +40 +40 +40 +40 +40 +40 +ENDCHAR +STARTCHAR FE8E +ENCODING 65166 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 4 10 0 4 +BITMAP +C0 +40 +40 +40 +40 +40 +40 +40 +30 +30 +ENDCHAR +STARTCHAR FE8F +ENCODING 65167 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 9 1 0 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0600 +0600 +ENDCHAR +STARTCHAR FE90 +ENCODING 65168 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 9 1 0 +BITMAP +8010 +8010 +8010 +8008 +FFF8 +7FF8 +0000 +0600 +0600 +ENDCHAR +STARTCHAR FE91 +ENCODING 65169 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 9 -1 0 +BITMAP +08 +08 +0C +04 +FC +FC +00 +10 +10 +ENDCHAR +STARTCHAR FE92 +ENCODING 65170 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 9 -1 0 +BITMAP +08 +08 +0C +04 +FC +FC +00 +10 +10 +ENDCHAR +STARTCHAR FE93 +ENCODING 65171 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 5 11 0 3 +BITMAP +10 +D0 +C0 +00 +20 +30 +78 +48 +48 +78 +70 +ENDCHAR +STARTCHAR FE94 +ENCODING 65172 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 14 0 3 +BITMAP +10 +50 +40 +00 +00 +10 +10 +28 +48 +F8 +E8 +08 +06 +06 +ENDCHAR +STARTCHAR FE95 +ENCODING 65173 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 9 1 3 +BITMAP +0300 +0F00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FE96 +ENCODING 65174 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 9 1 3 +BITMAP +0300 +0F00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FE97 +ENCODING 65175 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 10 -1 3 +BITMAP +08 +28 +20 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FE98 +ENCODING 65176 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 10 -1 3 +BITMAP +08 +28 +20 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FE99 +ENCODING 65177 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 10 1 3 +BITMAP +0400 +0700 +0F00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FE9A +ENCODING 65178 +SWIDTH 720 0 +DWIDTH 14 0 +BBX 13 10 1 3 +BITMAP +0400 +0700 +0F00 +0C00 +8010 +8010 +8010 +8008 +FFF8 +7FF8 +ENDCHAR +STARTCHAR FE9B +ENCODING 65179 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 3 +BITMAP +20 +28 +28 +20 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FE9C +ENCODING 65180 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 3 +BITMAP +20 +28 +28 +20 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FE9D +ENCODING 65181 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4000 +8600 +8600 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FE9E +ENCODING 65182 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 12 0 -4 +BITMAP +3F80 +7F80 +0E00 +31C0 +41C0 +8000 +8400 +8400 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FE9F +ENCODING 65183 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 9 -1 0 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0400 +0400 +ENDCHAR +STARTCHAR FEA0 +ENCODING 65184 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 9 -1 0 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +0000 +0400 +0400 +ENDCHAR +STARTCHAR FEA1 +ENCODING 65185 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 12 0 -4 +BITMAP +3F80 +7F80 +0C00 +3000 +4000 +8000 +8000 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FEA2 +ENCODING 65186 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 12 0 -4 +BITMAP +3F80 +7F80 +0E00 +31C0 +41C0 +8000 +8000 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FEA3 +ENCODING 65187 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 6 -1 3 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +ENDCHAR +STARTCHAR FEA4 +ENCODING 65188 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 6 -1 3 +BITMAP +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +ENDCHAR +STARTCHAR FEA5 +ENCODING 65189 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 9 16 0 -4 +BITMAP +0C00 +0400 +0000 +0000 +3F80 +7F80 +0C00 +3000 +4000 +8000 +8000 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FEA6 +ENCODING 65190 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 16 0 -4 +BITMAP +0400 +0400 +0000 +0000 +3F80 +7F80 +0E00 +31C0 +41C0 +8000 +8000 +8000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FEA7 +ENCODING 65191 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 10 -1 3 +BITMAP +0400 +0400 +0000 +0000 +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +ENDCHAR +STARTCHAR FEA8 +ENCODING 65192 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 10 -1 3 +BITMAP +0400 +0400 +0000 +0000 +1C00 +1F00 +0780 +01C0 +FFE0 +FFE0 +ENDCHAR +STARTCHAR FEA9 +ENCODING 65193 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 8 1 3 +BITMAP +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FEAA +ENCODING 65194 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 8 1 3 +BITMAP +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FEAB +ENCODING 65195 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 12 1 3 +BITMAP +40 +60 +00 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FEAC +ENCODING 65196 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 5 12 1 3 +BITMAP +40 +60 +00 +00 +20 +30 +18 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FEAD +ENCODING 65197 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 10 1 -2 +BITMAP +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR FEAE +ENCODING 65198 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 10 1 -2 +BITMAP +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR FEAF +ENCODING 65199 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 14 1 -2 +BITMAP +04 +04 +00 +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR FEB0 +ENCODING 65200 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 14 1 -2 +BITMAP +04 +04 +00 +00 +02 +03 +01 +01 +01 +01 +01 +03 +8E +78 +ENDCHAR +STARTCHAR FEB1 +ENCODING 65201 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 9 1 -1 +BITMAP +0104 +0182 +0092 +80FE +80FE +8080 +8180 +FF00 +7C00 +ENDCHAR +STARTCHAR FEB2 +ENCODING 65202 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 9 1 -1 +BITMAP +0104 +0182 +0092 +80FE +80FE +8080 +8180 +FF00 +7C00 +ENDCHAR +STARTCHAR FEB3 +ENCODING 65203 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 5 -1 3 +BITMAP +0020 +10A0 +08A0 +FFE0 +FF60 +ENDCHAR +STARTCHAR FEB4 +ENCODING 65204 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 5 -1 3 +BITMAP +0020 +10A0 +08A0 +FFE0 +FF60 +ENDCHAR +STARTCHAR FEB5 +ENCODING 65205 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 1 -1 +BITMAP +0030 +0038 +0028 +0020 +0000 +0104 +0182 +0092 +80FE +80FE +8080 +8180 +FF00 +7C00 +ENDCHAR +STARTCHAR FEB6 +ENCODING 65206 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 15 14 1 -1 +BITMAP +0030 +0038 +0028 +0020 +0000 +0104 +0182 +0092 +80FE +80FE +8080 +8180 +FF00 +7C00 +ENDCHAR +STARTCHAR FEB7 +ENCODING 65207 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 10 -1 3 +BITMAP +0100 +01C0 +0340 +0300 +0000 +0020 +10A0 +08A0 +FFE0 +FF60 +ENDCHAR +STARTCHAR FEB8 +ENCODING 65208 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 11 10 -1 3 +BITMAP +0100 +01C0 +0340 +0300 +0000 +0020 +10A0 +08A0 +FFE0 +FF60 +ENDCHAR +STARTCHAR FEB9 +ENCODING 65209 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 9 1 -1 +BITMAP +0101E0 +0187F0 +008C30 +80FFF0 +80FFF0 +808000 +810000 +FF0000 +7C0000 +ENDCHAR +STARTCHAR FEBA +ENCODING 65210 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 9 1 -1 +BITMAP +0101E0 +0187F0 +008C30 +80FFF0 +80FFF0 +808000 +810000 +FF0000 +7C0000 +ENDCHAR +STARTCHAR FEBB +ENCODING 65211 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 17 6 -1 3 +BITMAP +000700 +001F80 +103180 +08C180 +FFFF80 +FFFF80 +ENDCHAR +STARTCHAR FEBC +ENCODING 65212 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 17 6 -1 3 +BITMAP +000700 +001F80 +103180 +08C180 +FFFF80 +FFFF80 +ENDCHAR +STARTCHAR FEBD +ENCODING 65213 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 12 1 -1 +BITMAP +000800 +000800 +000000 +0101E0 +0187F0 +008C30 +80FFF0 +80FFF0 +808000 +810000 +FF0000 +7C0000 +ENDCHAR +STARTCHAR FEBE +ENCODING 65214 +SWIDTH 1080 0 +DWIDTH 21 0 +BBX 20 12 1 -1 +BITMAP +000800 +000800 +000000 +0101E0 +0187F0 +008C30 +80FFF0 +80FFF0 +808000 +810000 +FF0000 +7C0000 +ENDCHAR +STARTCHAR FEBF +ENCODING 65215 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 17 9 -1 3 +BITMAP +006000 +006000 +000000 +000700 +001F80 +103180 +08C180 +FFFF80 +FFFF80 +ENDCHAR +STARTCHAR FEC0 +ENCODING 65216 +SWIDTH 822 0 +DWIDTH 16 0 +BBX 17 9 -1 3 +BITMAP +006000 +006000 +000000 +000700 +001F80 +103180 +08C180 +FFFF80 +FFFF80 +ENDCHAR +STARTCHAR FEC1 +ENCODING 65217 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1800 +0800 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR FEC2 +ENCODING 65218 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1800 +0800 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR FEC3 +ENCODING 65219 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1800 +0800 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR FEC4 +ENCODING 65220 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1800 +0800 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR FEC5 +ENCODING 65221 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1880 +0880 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR FEC6 +ENCODING 65222 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1880 +0880 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR FEC7 +ENCODING 65223 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1880 +0880 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR FEC8 +ENCODING 65224 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 12 11 -1 4 +BITMAP +1800 +1880 +0880 +0800 +0800 +08E0 +0BF0 +0630 +0830 +FFF0 +FFF0 +ENDCHAR +STARTCHAR FEC9 +ENCODING 65225 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 16 0 -4 +BITMAP +3800 +7C00 +6000 +8000 +8100 +6700 +7C00 +3000 +2000 +4000 +8000 +8000 +C000 +6000 +7F80 +1F00 +ENDCHAR +STARTCHAR FECA +ENCODING 65226 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 9 13 0 -4 +BITMAP +1C00 +3C00 +1C00 +3C00 +2780 +4380 +4000 +8000 +4000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FECB +ENCODING 65227 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 6 -1 3 +BITMAP +1E00 +3F00 +2040 +3380 +FF00 +FC00 +ENDCHAR +STARTCHAR FECC +ENCODING 65228 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 6 -1 3 +BITMAP +1E +3E +3E +0C +FF +E3 +ENDCHAR +STARTCHAR FECD +ENCODING 65229 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 20 0 -4 +BITMAP +1000 +1000 +0000 +0000 +3800 +7C00 +6000 +8000 +8100 +6700 +7C00 +3000 +2000 +4000 +8000 +8000 +C000 +6000 +7F80 +1F00 +ENDCHAR +STARTCHAR FECE +ENCODING 65230 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 9 17 0 -4 +BITMAP +0800 +0800 +0000 +0000 +1C00 +3C00 +1C00 +3C00 +2780 +4380 +4000 +8000 +4000 +4000 +7000 +3F80 +1F00 +ENDCHAR +STARTCHAR FECF +ENCODING 65231 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 9 -1 3 +BITMAP +0C00 +0C00 +0000 +1E00 +3F00 +2040 +3380 +FF00 +FC00 +ENDCHAR +STARTCHAR FED0 +ENCODING 65232 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 10 -1 3 +BITMAP +08 +08 +00 +00 +1E +3E +3E +0C +FF +E3 +ENDCHAR +STARTCHAR FED1 +ENCODING 65233 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 10 0 3 +BITMAP +0018 +0018 +0000 +0000 +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR FED2 +ENCODING 65234 +SWIDTH 771 0 +DWIDTH 15 0 +BBX 15 10 0 3 +BITMAP +0018 +0018 +0000 +0000 +400C +800E +801A +C00E +7FFE +3FFE +ENDCHAR +STARTCHAR FED3 +ENCODING 65235 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 11 -1 3 +BITMAP +10 +10 +00 +18 +38 +2C +3C +3C +04 +FC +FC +ENDCHAR +STARTCHAR FED4 +ENCODING 65236 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 12 -1 3 +BITMAP +08 +08 +00 +00 +18 +1C +24 +24 +1C +0C +FC +FC +ENDCHAR +STARTCHAR FED5 +ENCODING 65237 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 1 -1 +BITMAP +0100 +0580 +0400 +0000 +0000 +0180 +03C0 +02C0 +83C0 +83C0 +8040 +80C0 +FF80 +7F00 +ENDCHAR +STARTCHAR FED6 +ENCODING 65238 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 14 1 -1 +BITMAP +0100 +0580 +0400 +0000 +0000 +0180 +03C0 +02C0 +83C0 +83C0 +8040 +80C0 +FF80 +7F00 +ENDCHAR +STARTCHAR FED7 +ENCODING 65239 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 12 -1 3 +BITMAP +08 +28 +20 +00 +18 +38 +2C +3C +3C +04 +FC +FC +ENDCHAR +STARTCHAR FED8 +ENCODING 65240 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 13 -1 3 +BITMAP +08 +28 +20 +00 +00 +18 +1C +24 +24 +1C +0C +FC +FC +ENDCHAR +STARTCHAR FED9 +ENCODING 65241 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 12 0 3 +BITMAP +0040 +0060 +0060 +0240 +0420 +0720 +0120 +0120 +9E20 +C020 +7FE0 +7FE0 +ENDCHAR +STARTCHAR FEDA +ENCODING 65242 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 12 0 3 +BITMAP +0040 +0060 +0060 +0240 +0420 +0720 +0120 +0120 +9E20 +C020 +7FE0 +7FE0 +ENDCHAR +STARTCHAR FEDB +ENCODING 65243 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 11 -1 3 +BITMAP +03 +0F +3C +20 +30 +38 +0C +02 +01 +FF +FF +ENDCHAR +STARTCHAR FEDC +ENCODING 65244 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 11 -1 3 +BITMAP +03 +0F +3C +20 +30 +38 +0C +02 +01 +FF +FF +ENDCHAR +STARTCHAR FEDD +ENCODING 65245 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 15 1 0 +BITMAP +0200 +0300 +0100 +0100 +0100 +0100 +0100 +0100 +8100 +8080 +8080 +8080 +8300 +FF00 +7E00 +ENDCHAR +STARTCHAR FEDE +ENCODING 65246 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 15 1 0 +BITMAP +0200 +0300 +0100 +0100 +0100 +0100 +0100 +0100 +8100 +8080 +8080 +8080 +8300 +FF00 +7E00 +ENDCHAR +STARTCHAR FEDF +ENCODING 65247 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 5 12 -1 3 +BITMAP +20 +38 +10 +10 +10 +10 +10 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FEE0 +ENCODING 65248 +SWIDTH 205 0 +DWIDTH 4 0 +BBX 5 12 -1 3 +BITMAP +20 +38 +10 +10 +10 +10 +10 +08 +08 +08 +F8 +F8 +ENDCHAR +STARTCHAR FEE1 +ENCODING 65249 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 11 0 -4 +BITMAP +38 +3C +0C +3C +7C +C0 +80 +80 +40 +40 +40 +ENDCHAR +STARTCHAR FEE2 +ENCODING 65250 +SWIDTH 308 0 +DWIDTH 6 0 +BBX 6 11 0 -4 +BITMAP +38 +3C +0C +3C +7C +C0 +80 +80 +40 +40 +40 +ENDCHAR +STARTCHAR FEE3 +ENCODING 65251 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 5 -1 3 +BITMAP +02 +0D +1D +FF +F7 +ENDCHAR +STARTCHAR FEE4 +ENCODING 65252 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 5 -1 3 +BITMAP +02 +0D +1D +FF +F7 +ENDCHAR +STARTCHAR FEE5 +ENCODING 65253 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 12 1 -1 +BITMAP +0800 +0800 +0000 +0000 +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR FEE6 +ENCODING 65254 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 12 1 -1 +BITMAP +0800 +0800 +0000 +0000 +0080 +0080 +8080 +8080 +8080 +8180 +FF00 +7E00 +ENDCHAR +STARTCHAR FEE7 +ENCODING 65255 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 9 -1 3 +BITMAP +10 +10 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FEE8 +ENCODING 65256 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 9 -1 3 +BITMAP +10 +10 +00 +08 +08 +0C +04 +FC +FC +ENDCHAR +STARTCHAR FEE9 +ENCODING 65257 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 4 7 1 3 +BITMAP +40 +E0 +F0 +90 +90 +F0 +E0 +ENDCHAR +STARTCHAR FEEA +ENCODING 65258 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 7 9 0 4 +BITMAP +10 +10 +28 +48 +F8 +E8 +08 +06 +06 +ENDCHAR +STARTCHAR FEEB +ENCODING 65259 +SWIDTH 462 0 +DWIDTH 9 0 +BBX 8 8 -1 3 +BITMAP +08 +1C +1C +26 +27 +19 +FF +E7 +ENDCHAR +STARTCHAR FEEC +ENCODING 65260 +SWIDTH 360 0 +DWIDTH 7 0 +BBX 8 10 -1 -1 +BITMAP +04 +0C +14 +18 +FF +FF +24 +34 +1E +0C +ENDCHAR +STARTCHAR FEED +ENCODING 65261 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 11 0 -2 +BITMAP +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FEEE +ENCODING 65262 +SWIDTH 411 0 +DWIDTH 8 0 +BBX 8 11 0 -2 +BITMAP +03 +07 +05 +05 +07 +03 +01 +01 +02 +86 +7C +ENDCHAR +STARTCHAR FEEF +ENCODING 65263 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 10 0 -1 +BITMAP +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +ENDCHAR +STARTCHAR FEF0 +ENCODING 65264 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 7 0 -2 +BITMAP +01E0 +43E0 +8200 +83C0 +C040 +7FC0 +3F00 +ENDCHAR +STARTCHAR FEF1 +ENCODING 65265 +SWIDTH 617 0 +DWIDTH 12 0 +BBX 12 13 0 -4 +BITMAP +0060 +00F0 +0100 +0200 +43E0 +81E0 +8020 +C1C0 +7F80 +3E00 +0300 +0B00 +0800 +ENDCHAR +STARTCHAR FEF2 +ENCODING 65266 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 11 9 0 -4 +BITMAP +01E0 +43E0 +8200 +83C0 +C040 +7FC0 +3F00 +0A00 +0A00 +ENDCHAR +STARTCHAR FEF3 +ENCODING 65267 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 10 -1 -1 +BITMAP +08 +08 +0C +04 +FC +FC +00 +08 +28 +20 +ENDCHAR +STARTCHAR FEF4 +ENCODING 65268 +SWIDTH 257 0 +DWIDTH 5 0 +BBX 6 10 -1 -1 +BITMAP +08 +08 +0C +04 +FC +FC +00 +08 +28 +20 +ENDCHAR +STARTCHAR FEF5 +ENCODING 65269 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 15 1 1 +BITMAP +E000 +0000 +8200 +C380 +E100 +6100 +1100 +0A00 +0A00 +0600 +0400 +0600 +1A00 +1E00 +1C00 +ENDCHAR +STARTCHAR FEF6 +ENCODING 65270 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 13 1 3 +BITMAP +E000 +0000 +C000 +F100 +7900 +0500 +0280 +0080 +0180 +0280 +0680 +3CC0 +F840 +ENDCHAR +STARTCHAR FEF7 +ENCODING 65271 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 10 15 0 1 +BITMAP +6000 +8000 +6100 +81C0 +0080 +4080 +6080 +3080 +0900 +0500 +0300 +0200 +0500 +0F00 +0E00 +ENDCHAR +STARTCHAR FEF8 +ENCODING 65272 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 13 1 3 +BITMAP +4000 +8000 +C000 +8100 +E100 +7900 +0480 +0280 +0180 +0280 +0680 +3CC0 +F840 +ENDCHAR +STARTCHAR FEF9 +ENCODING 65273 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 18 1 -4 +BITMAP +8200 +C380 +E100 +6100 +1100 +0A00 +0A00 +0600 +0400 +0600 +1A00 +1E00 +1C00 +0000 +1000 +2000 +1800 +2000 +ENDCHAR +STARTCHAR FEFA +ENCODING 65274 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 16 1 -2 +BITMAP +C000 +F100 +7900 +0500 +0280 +0080 +0180 +0280 +0680 +3CC0 +F840 +0000 +2000 +4000 +6000 +4000 +ENDCHAR +STARTCHAR FEFB +ENCODING 65275 +SWIDTH 514 0 +DWIDTH 10 0 +BBX 9 13 1 1 +BITMAP +8200 +C380 +E100 +6100 +1100 +0A00 +0A00 +0600 +0400 +0600 +1A00 +1E00 +1C00 +ENDCHAR +STARTCHAR FEFC +ENCODING 65276 +SWIDTH 565 0 +DWIDTH 11 0 +BBX 10 11 1 3 +BITMAP +C000 +F100 +7900 +0500 +0280 +0080 +0180 +0280 +0680 +3CC0 +F840 +ENDCHAR +STARTCHAR FFFC +ENCODING 65532 +SWIDTH 977 0 +DWIDTH 19 0 +BBX 19 14 0 0 +BITMAP +E71CE0 +800020 +800020 +000000 +1EF980 +B3C9A0 +A1F1A0 +A1C9A0 +B3C920 +1EFF00 +000000 +800020 +800020 +E71CE0 +ENDCHAR +ENDFONT diff --git a/TMC2209_Camera_Slider/CircuitPython/adafruit_tmc2209.py b/TMC2209_Camera_Slider/CircuitPython/adafruit_tmc2209.py new file mode 100644 index 000000000..e15672f47 --- /dev/null +++ b/TMC2209_Camera_Slider/CircuitPython/adafruit_tmc2209.py @@ -0,0 +1,768 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import time +import busio +from micropython import const +from generic_uart_device import GenericUARTDevice + +# pylint: disable=protected-access, too-many-public-methods, bare-except, too-many-function-args + +class TMC2209: + + GCONF = const(0x00) # Global configuration + GSTAT = const(0x01) # Global status flags + IFCNT = const(0x02) # Interface transmission counter + NODECONF = const(0x03) # Node configuration + OTP_PROG = const(0x04) # OTP programming + OTP_READ = const(0x05) # OTP read data + IOIN = const(0x06) + FACTORY_CONF = const(0x07) # Factory configuration + + # Velocity Dependent Control Registers + IHOLD_IRUN = const(0x10) + TPOWERDOWN = const(0x11) # Power down delay + TSTEP = const(0x12) # Time between steps + TPWMTHRS = const(0x13) # Upper velocity for StealthChop + TCOOLTHRS = const(0x14) # Lower threshold velocity for CoolStep + VACTUAL = const(0x22) + + # StallGuard Control Registers + SGTHRS = const(0x40) # StallGuard threshold + SG_RESULT = const(0x41) # StallGuard result + COOLCONF = const(0x42) # CoolStep configuration + + # Sequencer Registers + MSCNT = const(0x6A) + MSCURACT = const(0x6B) + + # Chopper Control Registers + DRV_STATUS = const(0x6F) # Driver status + PWMCONF = const(0x70) # StealthChop PWM config + PWM_SCALE = const(0x71) # PWM scaling values + PWM_AUTO = const(0x72) # PWM automatic configuration + + CHOPCONF = const(0x6C) + MRES_START = 24 # Microstepping resolution bits start position + MRES_LENGTH = 4 # Number of bits for microstepping resolution + + # GCONF register bit positions + GCONF_I_SCALE_ANALOG = const(0) + GCONF_INTERNAL_RSENSE = const(1) + GCONF_EN_SPREADCYCLE = const(2) + GCONF_SHAFT = const(3) + GCONF_INDEX_OTPW = const(4) + GCONF_INDEX_STEP = const(5) + GCONF_PDN_DISABLE = const(6) + GCONF_MSTEP_REG_SELECT = const(7) + GCONF_MULTISTEP_FILT = const(8) + GCONF_TEST_MODE = const(9) + + # GSTAT register bit positions + GSTAT_RESET = const(0) + GSTAT_DRV_ERR = const(1) + GSTAT_UV_CP = const(2) + + # CHOPCONF register bit positions + CHOPCONF_TOFF_START = const(0) + CHOPCONF_HSTRT_START = const(4) + CHOPCONF_HEND_START = const(7) + CHOPCONF_TBL_START = const(15) + CHOPCONF_VSENSE = const(17) + CHOPCONF_MRES_START = const(24) + CHOPCONF_INTPOL = const(28) + CHOPCONF_DEDGE = const(29) + CHOPCONF_DISS2G = const(30) + CHOPCONF_DISS2VS = const(31) + + # PWMCONF register bit positions + PWMCONF_PWM_OFS = const(0) + PWMCONF_PWM_GRAD = const(8) + PWMCONF_PWM_FREQ = const(16) + PWMCONF_PWM_AUTOSCALE = const(18) + PWMCONF_PWM_AUTOGRAD = const(19) + PWMCONF_FREEWHEEL = const(20) + PWMCONF_PWM_REG = const(24) + PWMCONF_PWM_LIM = const(28) + + # DRV_STATUS bit positions + DRV_STATUS_OTPW = const(0) + DRV_STATUS_OT = const(1) + DRV_STATUS_S2GA = const(2) + DRV_STATUS_S2GB = const(3) + DRV_STATUS_S2VSA = const(4) + DRV_STATUS_S2VSB = const(5) + DRV_STATUS_OLA = const(6) + DRV_STATUS_OLB = const(7) + DRV_STATUS_T120 = const(8) + DRV_STATUS_T143 = const(9) + DRV_STATUS_T150 = const(10) + DRV_STATUS_T157 = const(11) + DRV_STATUS_CS_ACTUAL = const(16) # Start bit for CS_ACTUAL + DRV_STATUS_STEALTH = const(30) + DRV_STATUS_STST = const(31) + + # COOLCONF register bit positions + COOLCONF_SEMIN = const(0) + COOLCONF_SEUP = const(5) + COOLCONF_SEMAX = const(8) + COOLCONF_SEDN = const(13) + COOLCONF_SEIMIN = const(15) + + def __init__(self, uart=None, tx_pin=None, rx_pin=None, addr=0, baudrate=115200): + + if uart is None and tx_pin is not None and rx_pin is not None: + uart = busio.UART(tx=tx_pin, rx=rx_pin, baudrate=baudrate, timeout=0.1) + elif uart is None: + raise ValueError("Either uart or tx_pin and rx_pin must be provided") + + self._addr = addr & 0x03 + + self._device = GenericUARTDevice( + uart=uart, + read_func=self._uart_read, + write_func=self._uart_write, + readreg_func=self.read_reg, + writereg_func=self.write_reg + ) + gconf = self.read_reg(self.GCONF) + gconf |= (1 << 7) + self.write_reg(self.GCONF, gconf) + ihold_irun = ( + (16 << 0) | # IHOLD = 16 (50% of max current) + (31 << 8) | # IRUN = 31 (max current) + (1 << 16) # IHOLDDELAY = 1 + ) + self.write_reg(self.IHOLD_IRUN, ihold_irun) + + self._step_count = 0 # Track full steps + self._last_mscnt = self.mscnt # Initialize last microstep counter + self._step_count = 0 + self._last_mscnt = 0 + try: + self._last_mscnt = self.mscnt + except: + pass + self._start_position = None + self._end_position = None + + @staticmethod + def calc_crc(data: bytes) -> int: + """Calculate CRC8-ATM + Polynomial: x^8 + x^2 + x + 1 (0x07) + """ + crc = 0 + for byte in data: + for _ in range(8): + if (crc >> 7) ^ (byte & 0x01): + crc = ((crc << 1) ^ 0x07) & 0xFF + else: + crc = (crc << 1) & 0xFF + byte = byte >> 1 + return crc + + def read_reg(self, reg_addr: int) -> int: + """Read a register value""" + while self._device._uart.in_waiting: + self._device._uart.read() + datagram = bytes([ + 0x05, + self._addr << 1, + reg_addr, + 0x00 + ]) + datagram = datagram[:-1] + bytes([self.calc_crc(datagram[:-1])]) + self._device._uart.write(datagram) + echo = bytearray(4) + if not self._device._uart.readinto(echo): + return 0 + response = bytearray(8) + if not self._device._uart.readinto(response): + return 0 + value = (response[3] << 24) | (response[4] << 16) | (response[5] << 8) | response[6] + return value + + def write_reg(self, reg_addr: int, value: int): + """Write a value to a register""" + if value < 0: + value = value & 0xFFFFFFFF + while self._device._uart.in_waiting: + self._device._uart.read() + data = value.to_bytes(4, 'big', signed=False) + datagram = bytes([ + 0x05, + self._addr << 1, + reg_addr | 0x80, + data[0], + data[1], + data[2], + data[3], + 0x00 + ]) + datagram = datagram[:-1] + bytes([self.calc_crc(datagram[:-1])]) + self._device._uart.write(datagram) + echo = bytearray(8) + self._device._uart.readinto(echo) + + def _uart_read(self, buffer: bytearray) -> int: + """Read raw data from UART""" + return self._device._uart.readinto(buffer) + + def _uart_write(self, buffer: bytes) -> int: + """Write raw data to UART""" + return self._device._uart.write(buffer) + + @property + def mscnt(self) -> int: + """Read the microstep counter + Returns the current position in the microstep table (0-1023) + """ + return self.read_reg(self.MSCNT) & 0x3FF # 10-bit value (0-1023) + + @property + def mscuract(self) -> tuple: + """Read the current microstep current for both phases + Returns: + tuple: (CUR_A, CUR_B) - Current for phase A and B (-255 to 255) + """ + value = self.read_reg(self.MSCURACT) + cur_a = (value >> 16) & 0x1FF + cur_b = value & 0x1FF + if cur_a & 0x100: + cur_a = -(cur_a ^ 0x1FF) - 1 + if cur_b & 0x100: + cur_b = -(cur_b ^ 0x1FF) - 1 + return (cur_a, cur_b) + + @property + def position(self) -> float: + """Get the current motor position in steps + This combines full steps tracked by software and microsteps from the driver + """ + return self._step_count + + def reset_position(self): + """Reset the position counter to zero at the current position""" + self._step_count = 0 + self._last_mscnt = self.mscnt + + @property + def version(self) -> int: + """Read chip version""" + ioin = self.read_reg(self.IOIN) + return (ioin >> 24) & 0xFF + + @property + def microsteps(self) -> int: + """Get current microsteps (1-256)""" + chopconf = self.read_reg(self.CHOPCONF) + mres = (chopconf >> self.MRES_START) & ((1 << self.MRES_LENGTH) - 1) + return 256 >> mres if mres <= 8 else 0 + + @microsteps.setter + def microsteps(self, steps: int): + """Set microsteps (1-256, will be rounded to power of 2)""" + steps = min(256, max(1, steps)) + mres = 0 + while steps < 256: + steps = steps << 1 + mres += 1 + + chopconf = self.read_reg(self.CHOPCONF) + mask = ((1 << self.MRES_LENGTH) - 1) << self.MRES_START + chopconf = (chopconf & ~mask) | ((mres & 0xF) << self.MRES_START) + + self.write_reg(self.CHOPCONF, chopconf) + + @property + def direction(self) -> bool: + """Get current direction (True = reversed, False = normal)""" + gconf = self.read_reg(self.GCONF) + return bool(gconf & (1 << 3)) + + @direction.setter + def direction(self, reverse: bool): + """Set motor direction (True = reversed, False = normal)""" + gconf = self.read_reg(self.GCONF) + if reverse: + gconf |= (1 << 3) + else: + gconf &= ~(1 << 3) + self.write_reg(self.GCONF, gconf) + + def rotate(self, velocity: int): + """Rotate the motor at a specific velocity + Args: + velocity: Rotation velocity (-2^23 to +2^23) + Positive for forward, negative for reverse + """ + # Clamp velocity to valid range (-2^23 to 2^23 - 1) + velocity = max(-(1 << 23), min((1 << 23) - 1, velocity)) + self.write_reg(self.VACTUAL, velocity) + + def step(self, steps: int, delay: float = 0.001): + """Step the motor a specific number of steps + Args: + steps: Number of steps (negative for reverse direction) + delay: Delay between steps in seconds + """ + direction = 1 if steps > 0 else -1 + for _ in range(abs(steps)): + self.rotate(10000 * direction) + time.sleep(delay) + self.stop() + # Update position counter + self._step_count += direction + + def stop(self): + """Stop the motor""" + self.write_reg(self.VACTUAL, 0) + + def set_current(self, run_current: int, hold_current: int = None): + """Set motor current + + Args: + run_current: Running current (0-31) + hold_current: Holding current (0-31), defaults to 50% of run_current + """ + if hold_current is None: + hold_current = run_current // 2 + + run_current = min(31, max(0, run_current)) + hold_current = min(31, max(0, hold_current)) + + ihold_irun = ( + (hold_current << 0) | # IHOLD + (run_current << 8) | # IRUN + (1 << 16) # IHOLDDELAY + ) + self.write_reg(self.IHOLD_IRUN, ihold_irun) + + @property + def stealth_chop_enabled(self): + """Check if StealthChop mode is enabled + + Returns: + bool: True if StealthChop is enabled, False if SpreadCycle is active + """ + gconf = self.read_reg(self.GCONF) + return not bool(gconf & (1 << self.GCONF_EN_SPREADCYCLE)) + + @stealth_chop_enabled.setter + def stealth_chop_enabled(self, enable): + """Enable or disable StealthChop mode (voltage PWM mode) + + Args: + enable (bool): True to enable StealthChop, False to use SpreadCycle + """ + gconf = self.read_reg(self.GCONF) + if enable: + # Clear EN_SPREADCYCLE bit to enable StealthChop + gconf &= ~(1 << self.GCONF_EN_SPREADCYCLE) + else: + # Set EN_SPREADCYCLE bit to enable SpreadCycle mode + gconf |= (1 << self.GCONF_EN_SPREADCYCLE) + self.write_reg(self.GCONF, gconf) + + @property + def pwm_threshold(self): + """Get the TSTEP threshold for switching to StealthChop + + Returns: + int: Current TSTEP threshold value + """ + return self.read_reg(self.TPWMTHRS) + + @pwm_threshold.setter + def pwm_threshold(self, threshold): + """Set the TSTEP threshold for switching to StealthChop + + This sets the upper velocity threshold for StealthChop operation. + When TSTEP falls below this value (higher velocity), the driver + will switch from StealthChop to SpreadCycle. + + Args: + threshold (int): TSTEP threshold value (0-1048575) + 0 = Disabled (use SpreadCycle only) + """ + # Clamp threshold to valid range + threshold = max(0, min(threshold, (1 << 20) - 1)) + self.write_reg(self.TPWMTHRS, threshold) + + @property + def pwm_scale(self): + """Read the PWM scaling values (results of StealthChop amplitude regulator) + + Returns: + tuple: (PWM_SCALE_SUM, PWM_SCALE_AUTO) where: + PWM_SCALE_SUM (0-255): Actual PWM duty cycle + PWM_SCALE_AUTO (-255 to +255): Signed offset from automatic amplitude regulation + """ + pwm_scale = self.read_reg(self.PWM_SCALE) + pwm_scale_sum = pwm_scale & 0xFF + pwm_scale_auto = (pwm_scale >> 16) & 0x1FF + if pwm_scale_auto & 0x100: + pwm_scale_auto = -(pwm_scale_auto ^ 0x1FF) - 1 + return (pwm_scale_sum, pwm_scale_auto) + + @property + def pwm_auto(self): + """Read the automatically generated PWM configuration values + + These values can be used as defaults for future configurations. + + Returns: + tuple: (PWM_OFS_AUTO, PWM_GRAD_AUTO) where: + PWM_OFS_AUTO (0-255): Automatically determined offset value + PWM_GRAD_AUTO (0-255): Automatically determined gradient value + """ + pwm_auto = self.read_reg(self.PWM_AUTO) + pwm_ofs_auto = pwm_auto & 0xFF + pwm_grad_auto = (pwm_auto >> 16) & 0xFF + + return (pwm_ofs_auto, pwm_grad_auto) + + def set_pwm_config(self, + pwm_offset=36, # PWM amplitude offset + pwm_gradient=0, # PWM amplitude gradient + pwm_freq=1, # PWM frequency setting + pwm_autoscale=True, # Automatic amplitude scaling + pwm_autograd=True, # Automatic gradient adaptation + freewheel_mode=0, # Standstill mode + pwm_reg=4, # Regulation loop gradient + pwm_lim=12): # PWM scale limit + """Configure StealthChop PWM mode parameters + + Args: + pwm_offset (int): PWM amplitude offset (0-255) + pwm_gradient (int): PWM amplitude gradient (0-255) + pwm_freq (int): PWM frequency selection (0-3) + 0: fPWM=2/1024 fCLK + 1: fPWM=2/683 fCLK + 2: fPWM=2/512 fCLK + 3: fPWM=2/410 fCLK + pwm_autoscale (bool): Enable automatic current scaling + pwm_autograd (bool): Enable automatic gradient adaptation + freewheel_mode (int): Standstill mode when motor current is 0 (0-3) + 0: Normal operation + 1: Freewheeling + 2: Coil shorted using LS drivers + 3: Coil shorted using HS drivers + pwm_reg (int): Regulation loop gradient (1-15) + pwm_lim (int): PWM automatic scale amplitude limit (0-15) + """ + pwm_offset = max(0, min(pwm_offset, 255)) + pwm_gradient = max(0, min(pwm_gradient, 255)) + pwm_freq = max(0, min(pwm_freq, 3)) + freewheel_mode = max(0, min(freewheel_mode, 3)) + pwm_reg = max(1, min(pwm_reg, 15)) + pwm_lim = max(0, min(pwm_lim, 15)) + pwmconf = ( + (pwm_lim & 0x0F) << self.PWMCONF_PWM_LIM | + (pwm_reg & 0x0F) << self.PWMCONF_PWM_REG | + (freewheel_mode & 0x03) << self.PWMCONF_FREEWHEEL | + (int(pwm_autograd) & 0x01) << self.PWMCONF_PWM_AUTOGRAD | + (int(pwm_autoscale) & 0x01) << self.PWMCONF_PWM_AUTOSCALE | + (pwm_freq & 0x03) << self.PWMCONF_PWM_FREQ | + (pwm_gradient & 0xFF) << self.PWMCONF_PWM_GRAD | + (pwm_offset & 0xFF) << self.PWMCONF_PWM_OFS + ) + + self.write_reg(self.PWMCONF, pwmconf) + + @property + def stall_threshold(self): + """Get the StallGuard threshold for stall detection + + Returns: + int: StallGuard threshold value (0-255) + Lower values = more sensitive + """ + return self.read_reg(self.SGTHRS) + + @stall_threshold.setter + def stall_threshold(self, threshold): + """Set the StallGuard threshold for stall detection + + Args: + threshold (int): StallGuard threshold value (0-255) + Lower values = more sensitive stall detection + """ + # Clamp threshold to valid range + threshold = max(0, min(threshold, 255)) + self.write_reg(self.SGTHRS, threshold & 0xFF) + + @property + def stall_guard_result(self): + """Read the current StallGuard result + + Returns: + int: StallGuard value (0-1023), higher = less motor load + A value of 0 typically indicates a stalled motor + """ + return self.read_reg(self.SG_RESULT) + + @property + def coolstep_threshold(self): + """Get the TSTEP threshold for enabling CoolStep and StallGuard + + Returns: + int: TSTEP threshold value (0-1048575) + """ + return self.read_reg(self.TCOOLTHRS) + + @coolstep_threshold.setter + def coolstep_threshold(self, threshold): + """Set the TSTEP threshold for enabling CoolStep and StallGuard + + When TSTEP is between TCOOLTHRS and TPWMTHRS: + - StallGuard output signal becomes enabled + - CoolStep becomes enabled (if configured) + + Args: + threshold (int): TSTEP threshold value (0-1048575) + 0 = Disabled + """ + threshold = max(0, min(threshold, (1 << 20) - 1)) + self.write_reg(self.TCOOLTHRS, threshold) + + def configure_coolstep(self, + semin=0, # Minimum StallGuard value (0-15) + semax=0, # StallGuard hysteresis (0-15) + sedn=0, # Current down step speed (0-3) + seup=0, # Current up step width (0-3) + seimin=False): # Minimum current setting + """Configure CoolStep adaptive current scaling + + Args: + semin (int): Minimum StallGuard value for smart current control (0-15) + 0 = CoolStep disabled + semax (int): StallGuard hysteresis value (0-15) + sedn (int): Current down step speed (0-3) + 0: Slowest (for each 32 StallGuard values) + 1: Down step for each 8 StallGuard values + 2: Down step for each 2 StallGuard values + 3: Down step for each StallGuard value + seup (int): Current up step width (0-3) + 0: 1 step per measurement + 1: 2 steps per measurement + 2: 4 steps per measurement + 3: 8 steps per measurement + seimin (bool): Minimum current setting + False = 1/2 of current setting (IRUN) + True = 1/4 of current setting (IRUN) + """ + semin = max(0, min(semin, 15)) + semax = max(0, min(semax, 15)) + sedn = max(0, min(sedn, 3)) + seup = max(0, min(seup, 3)) + + coolconf = ( + (int(seimin) & 0x01) << self.COOLCONF_SEIMIN | + (sedn & 0x03) << self.COOLCONF_SEDN | + (semax & 0x0F) << self.COOLCONF_SEMAX | + (seup & 0x03) << self.COOLCONF_SEUP | + (semin & 0x0F) << self.COOLCONF_SEMIN + ) + + self.write_reg(self.COOLCONF, coolconf) + + def is_stalled(self): + """Check if the motor has stalled based on StallGuard threshold + + Note: TCOOLTHRS must be set and StallGuard must be active + (stepper must be moving at a speed where TSTEP > TCOOLTHRS) + + Returns: + bool: True if motor is stalled, False otherwise + """ + if self.coolstep_threshold == 0: + return False + sg_result = self.stall_guard_result + threshold = self.stall_threshold * 2 + return sg_result <= threshold + + def get_temperature_status(self): + """Get the current temperature status of the driver + + Returns: + dict: Temperature flags + 'warning': True if temperature warning is active + 'shutdown': True if overtemperature shutdown is active + 't120': True if temperature exceeds 120Β°C + 't143': True if temperature exceeds 143Β°C + 't150': True if temperature exceeds 150Β°C + 't157': True if temperature exceeds 157Β°C + """ + status = self.read_reg(self.DRV_STATUS) + + return { + 'warning': bool(status & (1 << self.DRV_STATUS_OTPW)), + 'shutdown': bool(status & (1 << self.DRV_STATUS_OT)), + 't120': bool(status & (1 << self.DRV_STATUS_T120)), + 't143': bool(status & (1 << self.DRV_STATUS_T143)), + 't150': bool(status & (1 << self.DRV_STATUS_T150)), + 't157': bool(status & (1 << self.DRV_STATUS_T157)) + } + + @property + def driver_status(self): + """Read driver status flags + + Returns: + dict: Dictionary with status flags including: + - standstill: Motor standstill detected + - stealth_mode: Driver is in StealthChop mode + - overtemperature_warning: Temperature warning flag + - overtemperature_shutdown: Overtemperature shutdown flag + - short_to_ground: Short to ground detected + - low_side_short: Short on low side detected + - open_load: Open load detected + - temperature: Temperature status flags + - current_scaling: Actual current scaling value + """ + status = self.read_reg(self.DRV_STATUS) + + return { + 'standstill': bool(status & (1 << self.DRV_STATUS_STST)), + 'stealth_mode': bool(status & (1 << self.DRV_STATUS_STEALTH)), + 'overtemperature_warning': bool(status & (1 << self.DRV_STATUS_OTPW)), + 'overtemperature_shutdown': bool(status & (1 << self.DRV_STATUS_OT)), + 'short_to_ground': bool(status & ((1 << self.DRV_STATUS_S2GA) | + (1 << self.DRV_STATUS_S2GB))), + 'low_side_short': bool(status & ((1 << self.DRV_STATUS_S2VSA) | + (1 << self.DRV_STATUS_S2VSB))), + 'open_load': bool(status & ((1 << self.DRV_STATUS_OLA) | (1 << self.DRV_STATUS_OLB))), + 'temperature': { + 't120': bool(status & (1 << self.DRV_STATUS_T120)), + 't143': bool(status & (1 << self.DRV_STATUS_T143)), + 't150': bool(status & (1 << self.DRV_STATUS_T150)), + 't157': bool(status & (1 << self.DRV_STATUS_T157)) + }, + 'current_scaling': (status >> self.DRV_STATUS_CS_ACTUAL) & 0x1F + } + + @property + def global_status(self): + """Read global status flags + + Returns: + dict: Dictionary with global status flags: + - reset: Indicates IC has been reset + - driver_error: Driver has been shut down due to error + - undervoltage: Undervoltage on charge pump + """ + gstat = self.read_reg(self.GSTAT) + + return { + 'reset': bool(gstat & (1 << self.GSTAT_RESET)), + 'driver_error': bool(gstat & (1 << self.GSTAT_DRV_ERR)), + 'undervoltage': bool(gstat & (1 << self.GSTAT_UV_CP)) + } + + def clear_error_flags(self): + """Clear all error flags in GSTAT register + + This clears the reset, driver_error and undervoltage flags. + """ + # Write 1 to each bit to clear the flags + self.write_reg(self.GSTAT, 0x07) # 0b111 + + def interface_transmission_counter(self): + """Read the interface transmission counter + + This counter is incremented with each successful UART write access. + It can be used to verify that the UART communication is working correctly. + + Returns: + int: Number of successful UART transmissions (0-255) + """ + return self.read_reg(self.IFCNT) + + def set_freewheel_mode(self, mode): + """Set the freewheel mode for when motor current is 0 + + Args: + mode (int): Freewheel mode (0-3) + 0: Normal operation + 1: Freewheeling (motor can spin freely) + 2: Coil shorted using LS drivers (passive braking) + 3: Coil shorted using HS drivers + """ + mode = max(0, min(mode, 3)) + pwmconf = self.read_reg(self.PWMCONF) + pwmconf &= ~(0x03 << self.PWMCONF_FREEWHEEL) + pwmconf |= (mode & 0x03) << self.PWMCONF_FREEWHEEL + self.write_reg(self.PWMCONF, pwmconf) + + def set_pwm_frequency(self, freq): + """Set PWM frequency for StealthChop mode + + Args: + freq (int): PWM frequency selection (0-3) + 0: fPWM=2/1024 fCLK (~23kHz @ 12MHz clock) + 1: fPWM=2/683 fCLK (~35kHz @ 12MHz clock) + 2: fPWM=2/512 fCLK (~47kHz @ 12MHz clock) + 3: fPWM=2/410 fCLK (~58kHz @ 12MHz clock) + """ + freq = max(0, min(freq, 3)) + pwmconf = self.read_reg(self.PWMCONF) + pwmconf &= ~(0x03 << self.PWMCONF_PWM_FREQ) + pwmconf |= (freq & 0x03) << self.PWMCONF_PWM_FREQ + self.write_reg(self.PWMCONF, pwmconf) + + def release_motor(self): + """Release motor by setting hold current to zero + + This completely disables current to the motor when idle. + """ + ihold_irun = self.read_reg(self.IHOLD_IRUN) + ihold_irun &= ~0x1F + self.write_reg(self.IHOLD_IRUN, ihold_irun) + + def enable_motor(self, run_current=None): + """Enable the motor with specified current + + Args: + run_current: Run current 0-31 (None = use current setting) + """ + chopconf = self.read_reg(self.CHOPCONF) + if (chopconf & 0x0F) == 0: + chopconf = (chopconf & ~0x0F) | 4 + self.write_reg(self.CHOPCONF, chopconf) + pwmconf = self.read_reg(self.PWMCONF) + pwmconf &= ~(0x03 << self.PWMCONF_FREEWHEEL) # Clear freewheel bits + self.write_reg(self.PWMCONF, pwmconf) + if run_current is not None: + ihold_irun = self.read_reg(self.IHOLD_IRUN) + ihold_irun = (ihold_irun & ~(0x1F << 8)) | ((run_current & 0x1F) << 8) + self.write_reg(self.IHOLD_IRUN, ihold_irun) + + def disable_motor(self, mode="freewheel"): + """Disable the motor to prevent heat buildup + + Args: + mode: How to disable the motor + "release": Set current to zero but keep driver enabled + "freewheel": Set to freewheel mode and zero current + "powerdown": Completely disable driver (TOFF=0) + """ + if mode == "release": + ihold_irun = self.read_reg(self.IHOLD_IRUN) + ihold_irun &= ~0x1F + self.write_reg(self.IHOLD_IRUN, ihold_irun) + + elif mode == "freewheel": + ihold_irun = self.read_reg(self.IHOLD_IRUN) + ihold_irun &= ~0x1F + self.write_reg(self.IHOLD_IRUN, ihold_irun) + pwmconf = self.read_reg(self.PWMCONF) + pwmconf &= ~(0x03 << self.PWMCONF_FREEWHEEL) + pwmconf |= (1 << self.PWMCONF_FREEWHEEL) + self.write_reg(self.PWMCONF, pwmconf) + + elif mode == "powerdown": + chopconf = self.read_reg(self.CHOPCONF) + chopconf &= ~0x0F + self.write_reg(self.CHOPCONF, chopconf) diff --git a/TMC2209_Camera_Slider/CircuitPython/code.py b/TMC2209_Camera_Slider/CircuitPython/code.py new file mode 100644 index 000000000..89d5c755b --- /dev/null +++ b/TMC2209_Camera_Slider/CircuitPython/code.py @@ -0,0 +1,575 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import time +import supervisor +import rotaryio +import keypad +import board +import busio +import displayio +from adafruit_display_text import label +from fourwire import FourWire +from adafruit_st7789 import ST7789 +from adafruit_bitmap_font import bitmap_font +from adafruit_tmc2209 import TMC2209 + +displayio.release_displays() + +RAILS = 520 # length of rails in mm +microsteps = 128 +gear_ratio = 41 / 16 + +shot_velocities = [ + 20, + 15, + 10 +] + +keys = keypad.Keys((board.D2, board.A2, board.A3), value_when_pressed=False, pull=True) + +encoder = rotaryio.IncrementalEncoder(board.D7, board.D6) +last_position = None + +spi = board.SPI() +tft_cs = board.D10 +tft_dc = board.D8 + +display_bus = FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=board.D9) + +display = ST7789(display_bus, width=240, height=240, rowstart=80, auto_refresh=False) + +splash = displayio.Group() +display.root_group = splash + +bitmap = displayio.OnDiskBitmap(open("/icons.bmp", "rb")) + +grid_bg = displayio.TileGrid(bitmap, pixel_shader=bitmap.pixel_shader, + tile_height=100, tile_width=100, + x=(display.width - 100) // 2, + y=(display.height - 100) // 2) +splash.append(grid_bg) + +text_group = displayio.Group() +font = bitmap_font.load_font("/Arial-14.bdf") +title_text = "Camera Slider" +title_area = label.Label(font, text=title_text, color=0xFFFFFF) +title_area.anchor_point = (0.5, 0.0) +title_area.anchored_position = (display.width / 2, 25) +text_group.append(title_area) +splash.append(text_group) + +font = bitmap_font.load_font("/Arial-14.bdf") +text_area = label.Label(font, text="", color=0xFFFFFF) +text_area.anchor_point = (0.5, 1.0) +text_area.anchored_position = (display.width / 2, display.height - 25) +text_group.append(text_area) + +uart = busio.UART(tx=board.TX, rx=board.RX, baudrate=115200, timeout=0.1) + +driver1 = TMC2209(uart=uart, addr=0) +driver2 = TMC2209(tx_pin=board.D4, rx_pin=board.D5, addr=0) + +version1 = driver1.version +version2 = driver2.version +print(f"TMC2209 #1 Version: 0x{version1:02X}") +print(f"TMC2209 #2 Version: 0x{version2:02X}") + +driver1.microsteps = microsteps +print(driver1.microsteps) +driver2.microsteps = microsteps +print(driver2.microsteps) + +STEPS_PER_MM = 200 * microsteps / 8 +driver1.direction = False +driver2.direction = True + +last_pos = 0 +select = 0 +menu = 0 +time_mode = 0 +shot_mode = 0 +timelapse = True +movement_time = 0 +titles = ["Camera Slider", "Motor 1", "Motor 2", "Mode", + "Timelapse", "One-Shot", "Start?", "Running"] + +home_text = ["Press to Begin", "0"] +motor1_text = ["Slide to Start Point", "0"] +motor2_text = ["Move to Start", "Move to End", "0"] +mode_text = ["Timelapse", "One-Shot"] +time_text = ["1", "5", "10", "15", "30"] +shot_speeds = [10, 5, 2] +speeds = [] +shot_text = ["Slow", "Medium", "Fast"] +start_text = ["Go!", 0] +running_text = ["STOP!", "Pause/Resume"] +running_icons = [6, 7] +mode_icons = [3, 4] +sub_titles = [home_text, motor1_text, motor2_text, mode_text, + time_text, shot_text, start_text, running_text] +motor2_coordinates = [0.0, 0.0] +text_area.text = home_text[0] +display.refresh() + +def adv_menu(m): + m = (m + 1) % 8 + title_area.text = titles[m] + sub = sub_titles[m] + if m == 4: + grid_bg[0] = 3 + elif m == 5: + grid_bg[0] = 4 + elif m > 5: + grid_bg[0] = m - 1 + else: + grid_bg[0] = m + text_area.text = sub[0] + display.refresh() + return m + +motor1_movement = { + "is_active": False, + "current_step": 0, + "total_steps": 0, + "start_pos": 0, + "end_pos": 0, + "step_direction": 1, + "last_step_time": 0, + "step_interval": 0, + "is_paused": False, + "toggle_pause": False, + "stop_requested": False +} + +motor2_movement = { + "is_active": False, + "current_step": 0, + "total_steps": 0, + "start_pos": 0, + "end_pos": 0, + "step_direction": 1, + "last_step_time": 0, + "step_interval": 0, + "is_paused": False, + "toggle_pause": False, + "stop_requested": False +} + +# pylint: disable=too-many-branches, too-many-statements, inconsistent-return-statements + +def calculate_linear_velocity(steps_per_second, clock_frequency=12000000, + micro=128, scaling_factor=6): + frequency = steps_per_second * micro + vactual = int((frequency * (1 << 23)) / (clock_frequency * scaling_factor)) + vactual = max(-(1 << 23), min((1 << 23) - 1, vactual)) + return vactual + +def move_steps_over_time(camera_driver, start_position, end_position, + time_seconds, micro=128, ratio=None): + steps = abs(end_position - start_position) + + if camera_driver: + direction = -1 if end_position < start_position else 1 + time_seconds = time_seconds * 2 + else: + direction = 1 if driver1.direction else -1 + + if ratio is not None: + steps = steps / ratio + + total_microsteps = steps * micro + microsteps_per_second = total_microsteps / time_seconds + fCLK = 12000000 + if camera_driver: + vactual = int(microsteps_per_second / (fCLK / (1 << 24))) + else: + vactual = int(microsteps_per_second / (fCLK / (1 << 27))) + velocity = max(-(1 << 23), min((1 << 23) - 1, vactual)) + velocity *= direction + return velocity + +def calculate_timelapse_velocity(start_position, end_position, duration_seconds, micro=128, + clock_frequency=12000000, scaling_factor=6, min_velocity=100): + total_steps = abs(end_position - start_position) + steps_per_second = total_steps / duration_seconds + full_steps_per_second = steps_per_second / micro + vactual = calculate_linear_velocity(full_steps_per_second, clock_frequency, + micro, scaling_factor) + direction = -1 if end_position < start_position else 1 + if abs(vactual) < min_velocity and vactual != 0: + vactual = min_velocity * direction + return vactual + +def calculate_rail_velocity(total_steps, duration_sec, direction, + is_timelapse=True, micro=128, clock_frequency=12000000): + steps_per_second = total_steps / duration_sec + full_steps_per_second = steps_per_second / micro + if not is_timelapse: + base_scaling = 1.0 + min_velocity = 400 + vactual = int((full_steps_per_second * micro * (1 << 23)) + / (clock_frequency * base_scaling)) + vactual *= direction + if abs(vactual) < min_velocity: + vactual = min_velocity * direction + else: + base_scaling = 6.0 + min_velocity = 50 + vactual = int((full_steps_per_second * micro * (1 << 23)) / + (clock_frequency * base_scaling)) + vactual *= direction + if abs(vactual) < min_velocity: + vactual = min_velocity * direction + vactual = max(-(1 << 23), min((1 << 23) - 1, vactual)) + return vactual + +def move_motor_with_rotate(driver, movement_state, start_position=None, + end_position=None, duration_sec=0, micro=128): + if start_position is not None and end_position is not None and not movement_state["is_active"]: + if timelapse: + driver.enable_motor(run_current=20) + scaling_factor = 6 + min_velocity = 50 + velocity = calculate_timelapse_velocity( + start_position, + end_position, + duration_sec, + micro, + scaling_factor=scaling_factor, + min_velocity=min_velocity + ) + else: + driver.enable_motor(run_current=30) + velocity = calculate_rail_velocity( + int(RAILS*STEPS_PER_MM), + duration_sec, + movement_state["step_direction"], + is_timelapse=timelapse, + micro=micro + ) + initial_velocity = int(velocity * 0.2) + if abs(initial_velocity) < 200: + initial_velocity = 200 * (1 if velocity > 0 else -1) + driver.rotate(initial_velocity) + movement_state["initial_velocity"] = initial_velocity + movement_state["final_velocity"] = velocity + movement_state["ramp_up_done"] = False + movement_state["ramp_up_time"] = 500 + movement_state["total_steps"] = int(RAILS*STEPS_PER_MM) + movement_state["step_direction"] = 1 if end_position > start_position else -1 + movement_state["start_pos"] = 0 + movement_state["end_pos"] = int(RAILS*STEPS_PER_MM) + movement_state["movement_start_time"] = supervisor.ticks_ms() + movement_state["movement_duration_ms"] = duration_sec * 1000 + movement_state["is_active"] = True + movement_state["is_paused"] = False + return + movement_state["total_steps"] = int(RAILS*STEPS_PER_MM) + movement_state["step_direction"] = driver.direction + movement_state["start_pos"] = 0 + movement_state["end_pos"] = int(RAILS*STEPS_PER_MM) + + if duration_sec > 0 and movement_state["total_steps"] > 0: + movement_state["velocity"] = velocity + driver.rotate(velocity) + movement_state["movement_start_time"] = supervisor.ticks_ms() + movement_state["movement_duration_ms"] = duration_sec * 1000 + else: + default_velocity = 2000 * movement_state["step_direction"] + driver.rotate(default_velocity) + movement_state["movement_duration_ms"] = movement_state["total_steps"] * 10 + movement_state["movement_start_time"] = supervisor.ticks_ms() + movement_state["is_active"] = True + movement_state["is_paused"] = False + + if movement_state["is_active"] and movement_state["toggle_pause"]: + movement_state["is_paused"] = not movement_state["is_paused"] + movement_state["toggle_pause"] = False + if movement_state["is_paused"]: + driver.rotate(0) + movement_state["pause_time"] = supervisor.ticks_ms() + else: + elapsed_ms = movement_state["pause_time"] - movement_state["movement_start_time"] + remaining_ms = movement_state["movement_duration_ms"] - elapsed_ms + if remaining_ms > 0: + driver.rotate(movement_state["velocity"]) + movement_state["movement_start_time"] = supervisor.ticks_ms() - elapsed_ms + else: + driver.rotate(0) + driver.disable_motor() + movement_state["is_active"] = False + + if movement_state["is_active"] and movement_state["stop_requested"]: + driver.rotate(0) + driver.disable_motor() + movement_state["is_active"] = False + movement_state["stop_requested"] = False + return { + "active": False, + "complete": False, + "progress_percent": (supervisor.ticks_ms() - movement_state["movement_start_time"]) + / movement_state["movement_duration_ms"] * 100, + "stopped_by_user": True + } + + if movement_state["is_active"] and not movement_state["is_paused"]: + current_t = supervisor.ticks_ms() + e = current_t - movement_state["movement_start_time"] + if e >= movement_state["movement_duration_ms"]: + print("Movement time complete!") + driver.rotate(0) + driver.disable_motor() + movement_state["is_active"] = False + return { + "active": False, + "complete": True, + "progress_percent": 100, + "stopped_by_user": False + } + + return { + "active": movement_state["is_active"], + "paused": movement_state["is_paused"], + "progress_percent": (supervisor.ticks_ms() - movement_state["movement_start_time"]) / + movement_state["movement_duration_ms"] * 100 + if movement_state["is_active"] else 0, + "stopped_by_user": False + } + +def pause_resume_motor1(): + motor1_movement["toggle_pause"] = True + +def stop_motor1(): + driver1.disable_motor() + driver1.reset_position() + motor1_movement["stop_requested"] = True + +def pause_resume_motor2(): + motor2_movement["toggle_pause"] = True + +def stop_motor2(): + driver2.disable_motor() + driver2.reset_position() + motor2_movement["stop_requested"] = True + +def stop_all_motors(): + driver1.rotate(0) + driver2.rotate(0) + driver1.disable_motor() + driver2.disable_motor() + motor1_movement["is_active"] = False + motor2_movement["is_active"] = False + motor1_movement["stop_requested"] = False + motor2_movement["stop_requested"] = False + time.sleep(0.1) + +driver1.disable_motor() +driver1.reset_position() +driver2.reset_position() + +while True: + if motor1_movement["is_active"]: + current_time = supervisor.ticks_ms() + elapsed = current_time - motor1_movement["movement_start_time"] + if elapsed >= motor1_movement["movement_duration_ms"]: + driver1.rotate(0) + driver1.disable_motor() + motor1_movement["is_active"] = False + if motor2_movement["is_active"]: + current_time = supervisor.ticks_ms() + elapsed = current_time - motor2_movement["movement_start_time"] + if elapsed >= motor2_movement["movement_duration_ms"]: + driver2.rotate(0) + driver2.disable_motor() + motor2_movement["is_active"] = False + if menu == 7: + active_motors = 0 + progress1 = 0 + progress2 = 0 + if motor1_movement["is_active"]: + active_motors += 1 + current_time = supervisor.ticks_ms() + elapsed = current_time - motor1_movement["movement_start_time"] + progress1 = (elapsed / motor1_movement["movement_duration_ms"]) * 100 + if motor2_movement["is_active"]: + active_motors += 1 + current_time = supervisor.ticks_ms() + elapsed = current_time - motor2_movement["movement_start_time"] + progress2 = (elapsed / motor2_movement["movement_duration_ms"]) * 100 + if active_motors > 0: + avg_progress = (progress1 + progress2) / active_motors + text_area.text = f"{running_text[select]} {avg_progress:.1f}%" + display.refresh() + elif active_motors == 0 and (motor1_movement["movement_duration_ms"] > 0 + or motor2_movement["movement_duration_ms"] > 0): + text_area.text = "Movement Complete!" + display.refresh() + event = keys.events.get() + if event: + if event.pressed: + print(f"{event.key_number} pressed") + if event.key_number == 0: + if menu == 0: + menu = adv_menu(menu) + elif menu == 2: + if select == 0: + motor2_coordinates[select] = driver2.position + if select == 1: + motor2_coordinates[select] = driver2.position + select += 1 + text_area.text = motor2_text[select] + if select > 1: + select = 0 + menu = adv_menu(menu) + if motor2_coordinates[0] > motor2_coordinates[1]: + move = motor2_coordinates[0] - motor2_coordinates[1] + else: + move = motor2_coordinates[1] - motor2_coordinates[0] + move = -move + driver2.step(move) + elif menu == 3: + if select == 1: + timelapse = False + menu += 1 + select = 0 + else: + timelapse = True + menu = adv_menu(menu) + elif menu == 4: + menu += 1 + time_mode = select + menu = adv_menu(menu) + select = 0 + print(f"{time_text[time_mode]}, timelapse: {timelapse}") + elif menu == 5: + shot_mode = select + menu = adv_menu(menu) + select = 0 + print(f"{shot_text[shot_mode]}, timelapse: {timelapse}") + elif menu == 6: + menu = adv_menu(menu) + if timelapse: + movement_time = int(time_text[time_mode]) * 60 + print(f"starting a timelapse for {time_text[time_mode]} minutes") + status1 = move_motor_with_rotate( + driver1, + motor1_movement, + start_position=0, + end_position=int(RAILS * STEPS_PER_MM), + duration_sec=movement_time, + microsteps=microsteps + ) + if abs(motor2_coordinates[1] - motor2_coordinates[0]) > 0: + velocity2 = move_steps_over_time(camera_driver=True, + start_position=motor2_coordinates[0], + end_position=motor2_coordinates[1], + time_seconds=movement_time, + microsteps=microsteps, + ratio=gear_ratio) + print(f"driver2 velocity is: {velocity2}") + driver2.enable_motor(run_current=25) + driver2.rotate(velocity2) + motor2_movement["is_active"] = True + motor2_movement["start_pos"] = motor2_coordinates[0] + motor2_movement["end_pos"] = motor2_coordinates[1] + motor2_movement["movement_start_time"] = supervisor.ticks_ms() + motor2_movement["movement_duration_ms"] = movement_time * 1000 + motor2_movement["velocity"] = velocity2 + motor2_movement["total_steps"] = (abs(motor2_coordinates[1] - + motor2_coordinates[0])) + else: + print(f"starting a {shot_text[shot_mode]} one-shot") + movement_time = shot_velocities[shot_mode] + status1 = move_motor_with_rotate( + driver1, + motor1_movement, + start_position=0, + end_position=int(RAILS * STEPS_PER_MM), + duration_sec=movement_time, + microsteps=microsteps + ) + if abs(motor2_coordinates[1] - motor2_coordinates[0]) > 0: + velocity2 = move_steps_over_time(camera_driver=True, + start_position=motor2_coordinates[0], + end_position=motor2_coordinates[1], + time_seconds=movement_time, + microsteps=microsteps, + ratio=gear_ratio) + driver2.enable_motor(run_current=25) + driver2.rotate(velocity2) + motor2_movement["is_active"] = True + motor2_movement["start_pos"] = motor2_coordinates[0] + motor2_movement["end_pos"] = motor2_coordinates[1] + motor2_movement["movement_start_time"] = supervisor.ticks_ms() + motor2_movement["movement_duration_ms"] = movement_time * 1000 + motor2_movement["velocity"] = velocity2 + motor2_movement["total_steps"] = (abs(motor2_coordinates[1] - + motor2_coordinates[0])) + elif menu == 7: + if select == 0: + stop_all_motors() + text_area.text = "Stopping..." + menu = adv_menu(menu) + elif select == 1: + pause_resume_motor1() + pause_resume_motor2() + paused_state = motor1_movement["is_paused"] or motor2_movement["is_paused"] + text_area.text = "Paused" if paused_state else "Running" + display.refresh() + if event.key_number == 1: + if menu == 1: + driver1.direction = False + driver1.reset_position() + menu = adv_menu(menu) + elif menu == 7: + stop_all_motors() + menu = adv_menu(menu) + if event.key_number == 2: + if menu == 1: + driver1.direction = True + driver1.reset_position() + menu = adv_menu(menu) + elif menu == 7: + stop_all_motors() + menu = adv_menu(menu) + display.refresh() + pos = encoder.position + if pos != last_pos: + if pos > last_pos: + if menu == 2: + driver2.step(-10) + if menu == 3: + select = (select + 1) % 2 + text_area.text = mode_text[select] + grid_bg[0] = mode_icons[select] + if menu == 4: + select = (select + 1) % len(time_text) + text_area.text = time_text[select] + if menu == 5: + select = (select + 1) % len(shot_text) + text_area.text = shot_text[select] + if menu == 7: + select = (select + 1) % len(running_text) + text_area.text = running_text[select] + grid_bg[0] = running_icons[select] + else: + if menu == 2: + driver2.step(10) + if menu == 3: + select = (select - 1) % 2 + text_area.text = mode_text[select] + grid_bg[0] = mode_icons[select] + if menu == 4: + select = (select - 1) % len(time_text) + text_area.text = time_text[select] + if menu == 5: + select = (select - 1) % len(shot_text) + text_area.text = shot_text[select] + if menu == 7: + select = (select - 1) % len(running_text) + text_area.text = running_text[select] + grid_bg[0] = running_icons[select] + last_pos = pos + display.refresh() diff --git a/TMC2209_Camera_Slider/CircuitPython/generic_uart_device.py b/TMC2209_Camera_Slider/CircuitPython/generic_uart_device.py new file mode 100644 index 000000000..541623cc6 --- /dev/null +++ b/TMC2209_Camera_Slider/CircuitPython/generic_uart_device.py @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# pylint: disable=too-few-public-methods + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BusDevice.git" + +class GenericUARTDevice: + """Class for communicating with a device via generic UART read/write functions""" + + def __init__(self, uart, read_func=None, write_func=None, + readreg_func=None, writereg_func=None): + self._uart = uart + self._read_func = read_func + self._write_func = write_func + self._readreg_func = readreg_func + self._writereg_func = writereg_func + + def read(self, buffer: bytearray, length: int) -> int: + """Read raw data from device into buffer""" + if self._read_func is None: + return 0 + while self._uart.in_waiting: + self._uart.read() + return self._read_func(buffer, length) + + def write(self, buffer: bytes, length: int) -> int: + """Write raw data from buffer to device""" + if self._write_func is None: + return 0 + return self._write_func(buffer, length) + + def read_register(self, addr_buf: bytes, addr_len: int, + data_buf: bytearray, data_len: int) -> int: + """Read from device register""" + if self._readreg_func is None: + return 0 + return self._readreg_func(addr_buf, addr_len, data_buf, data_len) + + def write_register(self, addr_buf: bytes, addr_len: int, data_buf: bytes, data_len: int) -> int: + """Write to device register""" + if self._writereg_func is None: + return 0 + return self._writereg_func(addr_buf, addr_len, data_buf, data_len) diff --git a/TMC2209_Camera_Slider/CircuitPython/icons.bmp b/TMC2209_Camera_Slider/CircuitPython/icons.bmp new file mode 100644 index 000000000..49da8481f Binary files /dev/null and b/TMC2209_Camera_Slider/CircuitPython/icons.bmp differ diff --git a/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB.brd b/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB.brd new file mode 100644 index 000000000..7eae982ba --- /dev/null +++ b/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB.brd @@ -0,0 +1,43161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +EN +UART +INDX +DIAG +MS2 +MS1 +STEP +DIR +GND +VDD +EN +UART +INDX +DIAG +MS2 +MS1 +STEP +DIR +GND +VDD +D9 +D8 +D7 +D6 +D5 +D4 +D3 +D2 +GND +GND +RX +TX +D+ +D10 +MOSI +MISO +SCK +A0 +A1 +A2 +A3 +3V +RST +GND +RAW +D- +GND +3V +VIN +EN +V+ +GND +MOTOR 1 +MOTOR 2 +A3 +A2 +D3 +D4 +D2 +2.2K +TFT +DISPLAY +3.3V +GND +SCK +MI +MO +CS +RST +DC +D8 +D9 +D10 +3.3V +GND +V+ +MS1 +MS2 +MS2 +MS1 +3.3V +3.3V +KB2040 +USB +MPM3601 +Camera Slider +Helper +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +⚑ +⚑ +πŸ— +βš™οΈ +βš™οΈ +v0, April 2025 +πŸ“Έ +D6 +D7 +D4 +D5 +v1, April 2025 + + + + + + + + +<h2><b>microBuilder.eu</b> Eagle Footprint Library</h2> + +<p>Footprints for common components used in our projects and products. This is the same library that we use internally, and it is regularly updated. The newest version can always be found at <b>www.microBuilder.eu</b>. If you find this library useful, please feel free to purchase something from our online store. Please also note that all holes are optimised for metric drill bits!</p> + +<h3>Obligatory Warning</h3> +<p>While it probably goes without saying, there are no guarantees that the footprints or schematic symbols in this library are flawless, and we make no promises of fitness for production, prototyping or any other purpose. These libraries are provided for information puposes only, and are used at your own discretion. While we make every effort to produce accurate footprints, and many of the items found in this library have be proven in production, we can't make any promises of suitability for a specific purpose. If you do find any errors, though, please feel free to contact us at www.microbuilder.eu to let us know about it so that we can update the library accordingly!</p> + +<h3>License</h3> +<p>This work is placed in the public domain, and may be freely used for commercial and non-commercial work with the following conditions:</p> +<p>THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +</p> + + + + + + + + + + + + +3,0 + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + +<b>1x10 PIN HEADER</b> + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +<b>PIN HEADER</b> + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + +DJ Jack 2.0mm PTH Right-Angle + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +DC 2.0/2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>RESISTOR</b><p> +type 0309, grid 12.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + +>NAME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Generated from <b>thermalBonnet.sch</b><p> +by exp-lbrs.ulp + + +<b>JST XH Connector Long Pads (Package)</b><p> + +Wire to board connector. + +Pitch: 2,54 mm, (0.100")<p> +Number of pins: <b>2</b><b><P> + +<b>Created by Rembrandt Electronics</b><p> +<b>www.rembrandtelectronics.com</b><p> + + + + + + + + + +>NAME +>VALUE +1 + + + + + + + + + + + + + +<b>EAGLE Design Rules</b> +<p> +Die Standard-Design-Rules sind so gewΓ€hlt, dass sie fΓΌr +die meisten Anwendungen passen. Sollte ihre Platine +besondere Anforderungen haben, treffen Sie die erforderlichen +Einstellungen hier und speichern die Design Rules unter +einem neuen Namen ab. +<b>EAGLE Design Rules</b> +<p> +The default Design Rules have been set to cover +a wide range of applications. Your particular design +may have different requirements, so please make the +necessary adjustments and save your customized +design rules under a new name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Since Version 6.2.2 text objects can contain more than one line, +which will not be processed correctly with this version. + + +Since Version 8.2, EAGLE supports online libraries. The ids +of those online libraries will not be understood (or retained) +with this version. + + +Since Version 8.3, EAGLE supports URNs for individual library +assets (packages, symbols, and devices). The URNs of those assets +will not be understood (or retained) with this version. + + +Since Version 8.3, EAGLE supports the association of 3D packages +with devices in libraries, schematics, and board files. Those 3D +packages will not be understood (or retained) with this version. + + + diff --git a/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB.sch b/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB.sch new file mode 100644 index 000000000..aa8d40410 --- /dev/null +++ b/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB.sch @@ -0,0 +1,7570 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<h2><b>microBuilder.eu</b> Eagle Footprint Library</h2> + +<p>Footprints for common components used in our projects and products. This is the same library that we use internally, and it is regularly updated. The newest version can always be found at <b>www.microBuilder.eu</b>. If you find this library useful, please feel free to purchase something from our online store. Please also note that all holes are optimised for metric drill bits!</p> + +<h3>Obligatory Warning</h3> +<p>While it probably goes without saying, there are no guarantees that the footprints or schematic symbols in this library are flawless, and we make no promises of fitness for production, prototyping or any other purpose. These libraries are provided for information puposes only, and are used at your own discretion. While we make every effort to produce accurate footprints, and many of the items found in this library have be proven in production, we can't make any promises of suitability for a specific purpose. If you do find any errors, though, please feel free to contact us at www.microbuilder.eu to let us know about it so that we can update the library accordingly!</p> + +<h3>License</h3> +<p>This work is placed in the public domain, and may be freely used for commercial and non-commercial work with the following conditions:</p> +<p>THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +</p> + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>1x10 PIN HEADER</b> + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +1x10 2.0mm Header Pins + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +<p><b>Pin Headers</b><br/> +10 Pin, 0.1"/2.54mm pitch, SMT</p> +<p>4UCON: 06680</p> + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>Name +>Value + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +<b>PIN HEADER</b> + + + + + +>NAME +>VALUE + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<p><b>Pin Headers</b><br/> +4 Pin, 0.1"/2.54mm pitch, SMT</p> + + + + + +>NAME +>VALUE + + + + + + + +>NAME +>VALUE + + + + + + + +>NAME +>VALUE + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>PIN HEADER</b> + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>DRAWING_NAME +>LAST_DATE_TIME +>SHEET +Sheet: +Adafruit Industries + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Frame - A3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + +DJ Jack 2.0mm PTH Right-Angle + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +DC 2.0/2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + +2.0/2.1mm DC Jack - SMT +<p>4UConnector: 03267</p> +<p>Note: Small tRestrict polygon's were added to the ground pads to improve solderability when this part is used in combination with a ground pour. By default, Eagle will product four large bridges to the ground pour significantly increasing the heat distribution on the pads and preventing lead-free solder from reflowing in certain situations. For more details, see: http://www.microbuilder.eu/Blog/09-12-14/Reducing_Thermals_for_Large_Pads_in_Eagle.aspx</p> + + + + + + + + + + + + + + + + + + + + + + + + ++ +GND + + +>VALUE +>NAME + + + + + + + + + + + + + + +>NAME +>VALUE +DC 1.3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1 +2 +A +B +C +>VALUE +>NAME +GND + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + +<p><b>Rotary Encoder with Built-In Switch</b></p> +<p><ul> +<li>PEC11 - 24 Step, PEC11-4215F-S24 - http://www.adafruit.com/products/377</li> +</ul></p> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<p><b>DC Barrel Jack</b></p> +<p><b>DCJACK_1.3MM_PTH</b> - Through Hole 1.3mm Jack (4UConnector: 05536)</p> +<p><b>DCJACK_2MM_PTH</b> - Through Hole 2.0/2.1 Jack (4UConnector: 05537)</p> +<p><b>DCJACK_2MM_SMT</b> - SMT 2.0/2.1mm Jack(4UConnector: 03267)</p> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>Supply Symbols</b><p> + GND, VCC, 0V, +5V, -5V, etc.<p> + Please keep in mind, that these devices are necessary for the + automatic wiring of the supply signals.<p> + The pin name defined in the symbol is identical to the net which is to be wired automatically.<p> + In this library the device names are the same as the pin names of the symbols, therefore the correct signal names appear next to the supply symbols in the schematic.<p> + <author>Created by librarian@cadsoft.de</author> + + + + + +>VALUE + + + + + +>VALUE + + + + + +>VALUE + + + + + +>VALUE + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + +<b>SUPPLY SYMBOL</b> + + + + + + + + + + + + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip + + + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +chip, wave soldering + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.10 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.12 W + + + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.10 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.12 W + + + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +MELF 0.25 W + + + + + + + + + + +>NAME +>VALUE + + + + + +<b>RESISTOR</b><p> +type 0204, grid 5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0204, grid 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0207, grid 10 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0207, grid 12 mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b><p> +type 0207, grid 15mm + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b><p> +type 0207, grid 2.5 mm + + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0207, grid 5 mm + + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0207, grid 7.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0309, grid 10mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0309, grid 12.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0411, grid 12.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0411, grid 15 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0411, grid 3.81 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0414, grid 15 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0414, grid 5 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0617, grid 17.5 mm + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0617, grid 22.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0617, grid 5 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0922, grid 22.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<b>RESISTOR</b><p> +type 0613, grid 5 mm + + + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0613, grid 15 mm + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type 0817, grid 22.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +0817 + + + + +<b>RESISTOR</b><p> +type 0817, grid 6.35 mm + + + + + + +>NAME +>VALUE +0817 + + + +<b>RESISTOR</b><p> +type V234, grid 12.5 mm + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type V235, grid 17.78 mm + + + + + + + + + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b><p> +type V526-0, grid 2.5 mm + + + + + + + + + + +>NAME +>VALUE + + +<b>Mini MELF 0102 Axial</b> + + + + +>NAME +>VALUE + + + +<b>RESISTOR</b><p> +type 0922, grid 7.5 mm + + + + + + +>NAME +>VALUE +0922 + + + +<b>CECC Size RC2211</b> Reflow Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC2211</b> Wave Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC3715</b> Reflow Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC3715</b> Wave Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC6123</b> Reflow Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>CECC Size RC6123</b> Wave Soldering<p> +source Beyschlag + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type RDH, grid 15 mm + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +RDH + + + + +<b>RESISTOR</b><p> +type 0204, grid 2.5 mm + + + + + + +>NAME +>VALUE + + +<b>RESISTOR</b><p> +type 0309, grid 2.5 mm + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + +<B>RESISTOR</B>, American symbol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + + + + + + + + + + + + + + + + + + + + +3.5mm Terminal block +<p>http://www.ladyada.net/library/pcb/eaglelibrary.html<p> + + + + + + + + + + + + + + + + + + + + + +Generated from <b>thermalBonnet.sch</b><p> +by exp-lbrs.ulp + + +<b>JST XH Connector Long Pads (Package)</b><p> + +Wire to board connector. + +Pitch: 2,54 mm, (0.100")<p> +Number of pins: <b>2</b><b><P> + +<b>Created by Rembrandt Electronics</b><p> +<b>www.rembrandtelectronics.com</b><p> + + + + + + + + + +>NAME +>VALUE +1 + + +<b>JST XH Connector Round Pads (Package)</b><p> + +Wire to board connector. + +Pitch: 2,54 mm, (0.100")<p> +Number of pins: <b>2</b><b><P> + +<b>Created by Rembrandt Electronics</b><p> +<b>www.rembrandtelectronics.com</b><p> + + + + + + + + + +>NAME +>VALUE +1 + + + + + + +>NAME +>VALUE + + + + +>NAME + + + + +<b>JST XH Connector 2 Pin</b><p> + +Wire to board connector. + +Pitch: 2,54 mm, (0.100")<p> +Number of pins: <b>2</b><b><P> + +<b>Created by Rembrandt Electronics</b><p> +<b>www.rembrandtelectronics.com</b><p> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Power + + +TFT + + + + +TMC2209 Breakouts +KB2040 + + +JST Connectors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Since Version 8.2, EAGLE supports online libraries. The ids +of those online libraries will not be understood (or retained) +with this version. + + +Since Version 8.3, EAGLE supports URNs for individual library +assets (packages, symbols, and devices). The URNs of those assets +will not be understood (or retained) with this version. + + +Since Version 8.3, EAGLE supports the association of 3D packages +with devices in libraries, schematics, and board files. Those 3D +packages will not be understood (or retained) with this version. + + + diff --git a/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB_2025-05-12.zip b/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB_2025-05-12.zip new file mode 100644 index 000000000..4d11ecdca Binary files /dev/null and b/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB_2025-05-12.zip differ diff --git a/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB_noPinguin.brd b/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB_noPinguin.brd new file mode 100644 index 000000000..861a9b1ab --- /dev/null +++ b/TMC2209_Camera_Slider/PCB_Files/TMC2209_Camera_Slider_PCB_revB_noPinguin.brd @@ -0,0 +1,42002 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +EN +UART +INDX +DIAG +MS2 +MS1 +STEP +DIR +GND +VDD +EN +UART +INDX +DIAG +MS2 +MS1 +STEP +DIR +GND +VDD +D9 +D8 +D7 +D6 +D5 +D4 +D3 +D2 +GND +GND +RX +TX +D+ +D10 +MOSI +MISO +SCK +A0 +A1 +A2 +A3 +3V +RST +GND +RAW +D- +GND +3V +VIN +EN +V+ +GND +MOTOR 1 +MOTOR 2 +A3 +A2 +D3 +D4 +D2 +2.2K +TFT +DISPLAY +3.3V +GND +SCK +MI +MO +CS +RST +DC +D8 +D9 +D10 +3.3V +GND +V+ +MS1 +MS2 +MS2 +MS1 +3.3V +3.3V +KB2040 +USB +MPM3601 +Camera Slider +Helper +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸ“· +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +πŸŽ₯ +⚑ +⚑ +πŸ— +βš™οΈ +βš™οΈ +v0, April 2025 +πŸ“Έ +D6 +D7 +D4 +D5 +v1, April 2025 + + + +<h2><b>microBuilder.eu</b> Eagle Footprint Library</h2> + +<p>Footprints for common components used in our projects and products. This is the same library that we use internally, and it is regularly updated. The newest version can always be found at <b>www.microBuilder.eu</b>. If you find this library useful, please feel free to purchase something from our online store. Please also note that all holes are optimised for metric drill bits!</p> + +<h3>Obligatory Warning</h3> +<p>While it probably goes without saying, there are no guarantees that the footprints or schematic symbols in this library are flawless, and we make no promises of fitness for production, prototyping or any other purpose. These libraries are provided for information puposes only, and are used at your own discretion. While we make every effort to produce accurate footprints, and many of the items found in this library have be proven in production, we can't make any promises of suitability for a specific purpose. If you do find any errors, though, please feel free to contact us at www.microbuilder.eu to let us know about it so that we can update the library accordingly!</p> + +<h3>License</h3> +<p>This work is placed in the public domain, and may be freely used for commercial and non-commercial work with the following conditions:</p> +<p>THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +</p> + + + + + + + + + + + + +3,0 + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + +<b>1x10 PIN HEADER</b> + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +<b>PIN HEADER</b> + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + +DJ Jack 2.0mm PTH Right-Angle + + + + + + + + + + + + + + + + + + +>NAME +>VALUE +DC 2.0/2.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>RESISTOR</b><p> +type 0309, grid 12.5 mm + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + +>NAME + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Generated from <b>thermalBonnet.sch</b><p> +by exp-lbrs.ulp + + +<b>JST XH Connector Long Pads (Package)</b><p> + +Wire to board connector. + +Pitch: 2,54 mm, (0.100")<p> +Number of pins: <b>2</b><b><P> + +<b>Created by Rembrandt Electronics</b><p> +<b>www.rembrandtelectronics.com</b><p> + + + + + + + + + +>NAME +>VALUE +1 + + + + + + + + + + + + + +<b>EAGLE Design Rules</b> +<p> +Die Standard-Design-Rules sind so gewΓ€hlt, dass sie fΓΌr +die meisten Anwendungen passen. Sollte ihre Platine +besondere Anforderungen haben, treffen Sie die erforderlichen +Einstellungen hier und speichern die Design Rules unter +einem neuen Namen ab. +<b>EAGLE Design Rules</b> +<p> +The default Design Rules have been set to cover +a wide range of applications. Your particular design +may have different requirements, so please make the +necessary adjustments and save your customized +design rules under a new name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Since Version 6.2.2 text objects can contain more than one line, +which will not be processed correctly with this version. + + +Since Version 8.2, EAGLE supports online libraries. The ids +of those online libraries will not be understood (or retained) +with this version. + + +Since Version 8.3, EAGLE supports URNs for individual library +assets (packages, symbols, and devices). The URNs of those assets +will not be understood (or retained) with this version. + + +Since Version 8.3, EAGLE supports the association of 3D packages +with devices in libraries, schematics, and board files. Those 3D +packages will not be understood (or retained) with this version. + + + diff --git a/Toy_Robot_Xylophone/code.py b/Toy_Robot_Xylophone/code.py new file mode 100644 index 000000000..883db2585 --- /dev/null +++ b/Toy_Robot_Xylophone/code.py @@ -0,0 +1,97 @@ +# SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import os +from random import randint +import board +import usb_midi +import keypad +from adafruit_mcp230xx.mcp23017 import MCP23017 +import adafruit_midi +from adafruit_midi.note_on import NoteOn +from adafruit_midi.note_off import NoteOff +import adafruit_midi_parser + +# music_box plays back MIDI files on CP drive +# set to false for live MIDI over USB control +music_box = True +# define the notes that correspond to each solenoid +notes = [48, 50, 52, 53, 55, 57, 59, 60] + +key = keypad.Keys((board.BUTTON,), value_when_pressed=False, pull=True) + +i2c = board.STEMMA_I2C() +mcp = MCP23017(i2c) +noids = [] +for i in range(8): + noid = mcp.get_pin(i) + noid.switch_to_output(value=False) + noids.append(noid) +# pylint: disable=used-before-assignment, unused-argument, global-statement, no-self-use +if not music_box: + midi = adafruit_midi.MIDI( + midi_in=usb_midi.ports[0], in_channel=0, midi_out=usb_midi.ports[1], out_channel=0 + ) +else: + midi_files = [] + for filename in os.listdir('/'): + if filename.lower().endswith('.mid') and not filename.startswith('.'): + midi_files.append("/"+filename) + print(midi_files) + + class Custom_Player(adafruit_midi_parser.MIDIPlayer): + def on_note_on(self, note, velocity, channel): # noqa: PLR6301 + for z in range(len(notes)): + if notes[z] == note: + print(f"Playing note: {note}") + noids[z].value = True + + def on_note_off(self, note, velocity, channel): # noqa: PLR6301 + for z in range(len(notes)): + if notes[z] == note: + noids[z].value = False + + def on_end_of_track(self, track): # noqa: PLR6301 + print(f"End of track {track}") + for z in range(8): + noids[z].value = False + + def on_playback_complete(self): # noqa: PLR6301 + global now_playing + now_playing = False + for z in range(8): + noids[z].value = False + parser = adafruit_midi_parser.MIDIParser() + parser.parse(midi_files[randint(0, (len(midi_files) - 1))]) + player = Custom_Player(parser) + new_file = False + now_playing = False + +while True: + if music_box: + event = key.events.get() + if event: + if event.pressed: + now_playing = not now_playing + if now_playing: + new_file = True + if new_file: + parser.parse(midi_files[randint(0, (len(midi_files) - 1))]) + print(f"Successfully parsed! Found {len(parser.events)} events.") + print(f"BPM: {parser.bpm:.1f}") + print(f"Note Count: {parser.note_count}") + new_file = False + if now_playing: + player.play(loop=False) + + else: + msg = midi.receive() + if msg is not None: + for i in range(8): + noid_output = noids[i] + notes_played = notes[i] + if isinstance(msg, NoteOn) and msg.note == notes_played: + noid_output.value = True + elif isinstance(msg, NoteOff) and msg.note == notes_played: + noid_output.value = False diff --git a/Toy_Robot_Xylophone/song_1.mid b/Toy_Robot_Xylophone/song_1.mid new file mode 100644 index 000000000..87bbc1b8c Binary files /dev/null and b/Toy_Robot_Xylophone/song_1.mid differ diff --git a/Toy_Robot_Xylophone/song_2.mid b/Toy_Robot_Xylophone/song_2.mid new file mode 100644 index 000000000..bc4602a4b Binary files /dev/null and b/Toy_Robot_Xylophone/song_2.mid differ diff --git a/Toy_Robot_Xylophone/song_3.mid b/Toy_Robot_Xylophone/song_3.mid new file mode 100644 index 000000000..46fda0d23 Binary files /dev/null and b/Toy_Robot_Xylophone/song_3.mid differ diff --git a/Toy_Robot_Xylophone/song_4.mid b/Toy_Robot_Xylophone/song_4.mid new file mode 100644 index 000000000..0ee97a9a3 Binary files /dev/null and b/Toy_Robot_Xylophone/song_4.mid differ diff --git a/Wiz5500_Ethernet_Breakout/Arduino_Wiz5500_Breakout/.uno.test.only b/Wiz5500_Ethernet_Breakout/Arduino_Wiz5500_Breakout/.uno.test.only new file mode 100644 index 000000000..e69de29bb diff --git a/Wiz5500_Ethernet_Breakout/Arduino_Wiz5500_Breakout/Arduino_Wiz5500_Breakout.ino b/Wiz5500_Ethernet_Breakout/Arduino_Wiz5500_Breakout/Arduino_Wiz5500_Breakout.ino new file mode 100644 index 000000000..584410a8c --- /dev/null +++ b/Wiz5500_Ethernet_Breakout/Arduino_Wiz5500_Breakout/Arduino_Wiz5500_Breakout.ino @@ -0,0 +1,135 @@ +// SPDX-FileCopyrightText: 2025 Liz Clark for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +/* + Web client + + This sketch connects to a website (http://wifitest.adafruit.com) + using an Adafruit Wiz5500 Ethernet Breakout + + Circuit: + * Ethernet breakout attached to pins 10, 11, 12, 13 + + created 18 Dec 2009 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe, based on work by Adrian McEwen + modified 10 June 2025 + by Liz Clark + + */ + +#include +#include + +// Enter a MAC address for your controller below. +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; + +char server[] = "wifitest.adafruit.com"; + +// Set the static IP address to use if the DHCP fails to assign +IPAddress ip(192, 168, 0, 177); +IPAddress myDns(192, 168, 0, 1); + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 80 is default for HTTP): +EthernetClient client; + +// Variables to measure the speed +unsigned long beginMicros, endMicros; +unsigned long byteCount = 0; +bool printWebData = true; // set to false for better speed measurement + +void setup() { + // You can use Ethernet.init(pin) to configure the CS pin + // Here you're using pin 10 for CS + // SCK: 13, MISO: 12, MOSI: 11 + Ethernet.init(10); // Most Arduino shields + + // Open serial communications and wait for port to open: + Serial.begin(115200); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // start the Ethernet connection: + Serial.println("Initialize Ethernet with DHCP:"); + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // Check for Ethernet hardware present + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + Serial.println("Ethernet breakout was not found. Check your wiring."); + while (true) { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + if (Ethernet.linkStatus() == LinkOFF) { + Serial.println("Ethernet cable is not connected."); + } + // try to configure using IP address instead of DHCP: + Ethernet.begin(mac, ip, myDns); + } else { + Serial.print(" DHCP assigned IP "); + Serial.println(Ethernet.localIP()); + } + // give the Ethernet a second to initialize: + delay(1000); + Serial.print("connecting to "); + Serial.print(server); + Serial.println("..."); + + // if you get a connection, report back via serial: + if (client.connect(server, 80)) { + Serial.print("connected to "); + Serial.println(client.remoteIP()); + // Make a HTTP request: + client.println("GET /testwifi/index.html HTTP/1.1"); + client.println("Host: wifitest.adafruit.com"); + client.println("Connection: close"); + client.println(); + } else { + // if you didn't get a connection to the server: + Serial.println("connection failed"); + } + beginMicros = micros(); +} + +void loop() { + // if there are incoming bytes available + // from the server, read them and print them: + int len = client.available(); + if (len > 0) { + byte buffer[80]; + if (len > 80) len = 80; + client.read(buffer, len); + if (printWebData) { + Serial.write(buffer, len); // show in the serial monitor (slows some boards) + } + byteCount = byteCount + len; + } + + // if the server's disconnected, stop the client: + if (!client.connected()) { + endMicros = micros(); + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + Serial.print("Received "); + Serial.print(byteCount); + Serial.print(" bytes in "); + float seconds = (float)(endMicros - beginMicros) / 1000000.0; + Serial.print(seconds, 4); + float rate = (float)byteCount / seconds / 1000.0; + Serial.print(", rate = "); + Serial.print(rate); + Serial.print(" kbytes/second"); + Serial.println(); + + // do nothing forevermore: + while (true) { + delay(1); + } + } +} diff --git a/Wiz5500_Ethernet_Breakout/CircuitPython_Wiz5500_Breakout/code.py b/Wiz5500_Ethernet_Breakout/CircuitPython_Wiz5500_Breakout/code.py new file mode 100644 index 000000000..c683fbad4 --- /dev/null +++ b/Wiz5500_Ethernet_Breakout/CircuitPython_Wiz5500_Breakout/code.py @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import adafruit_connection_manager +import adafruit_requests +import board +import busio +import digitalio + +from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K + +print("Wiz5100 Breakout WebClient Test") + +TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html" + +cs = digitalio.DigitalInOut(board.D10) +spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialize ethernet interface with DHCP +eth = WIZNET5K(spi_bus, cs) + +# Initialize a requests session +pool = adafruit_connection_manager.get_radio_socketpool(eth) +ssl_context = adafruit_connection_manager.get_radio_ssl_context(eth) +requests = adafruit_requests.Session(pool, ssl_context) + +print("Chip Version:", eth.chip) +print("MAC Address:", [hex(i) for i in eth.mac_address]) +print("My IP address is:", eth.pretty_ip(eth.ip_address)) +print("IP lookup adafruit.com: %s" % eth.pretty_ip(eth.get_host_by_name("adafruit.com"))) + + +# eth._debug = True +print("Fetching text from", TEXT_URL) +r = requests.get(TEXT_URL) +print("-" * 40) +print(r.text) +print("-" * 40) +r.close() + +print("Done!") diff --git a/World_Clock_Round_Display/README.md b/World_Clock_Round_Display/README.md new file mode 100644 index 000000000..a92bdf0db --- /dev/null +++ b/World_Clock_Round_Display/README.md @@ -0,0 +1,3 @@ +This code is for the Adafruit Learning System Guide "World clock" + +https://learn.adafruit.com/world-clock/overview diff --git a/World_Clock_Round_Display/circuitpython_world.bmp b/World_Clock_Round_Display/circuitpython_world.bmp new file mode 100644 index 000000000..12851f329 Binary files /dev/null and b/World_Clock_Round_Display/circuitpython_world.bmp differ diff --git a/World_Clock_Round_Display/code.py b/World_Clock_Round_Display/code.py new file mode 100644 index 000000000..4db00be5e --- /dev/null +++ b/World_Clock_Round_Display/code.py @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: 2025 Ben Everard for Adafruit Industries +# +# SPDX-License-Identifier: MIT +'''Display a world clock on a round LCD''' + +import os +import time +import board +import displayio +import fourwire +from adafruit_gc9a01a import GC9A01A +import wifi +import adafruit_ntp +import adafruit_connection_manager + +wifi.radio.connect(ssid=os.getenv('CIRCUITPY_WIFI_SSID'), + password=os.getenv('CIRCUITPY_WIFI_PASSWORD')) + +pool = adafruit_connection_manager.get_radio_socketpool(wifi.radio) +ntp = adafruit_ntp.NTP(pool, tz_offset=0, cache_seconds=3600) + +displayio.release_displays() +spi = board.SPI() +tft_cs = board.TX +tft_dc = board.RX + +display_bus = fourwire.FourWire( + spi, command=tft_dc, chip_select=tft_cs, reset=None +) + +display = GC9A01A(display_bus, width=240, height=240, auto_refresh=False) + +world = displayio.OnDiskBitmap("/world.bmp") + +tile_grid_1 = displayio.TileGrid(world, pixel_shader=world.pixel_shader) +tile_grid_2 = displayio.TileGrid(world, pixel_shader=world.pixel_shader) + +group = displayio.Group() + +group.append(tile_grid_1) +group.append(tile_grid_2) +display.root_group = group + +# Loop forever so you can enjoy your image +while True: + tile_grid_1.x = (20*ntp.datetime.tm_hour)+120 + tile_grid_2.x = tile_grid_1.x-480 + display.refresh() + time.sleep(60) diff --git a/library.deps b/library.deps index 7e2ca5528..7668ac9b4 100644 --- a/library.deps +++ b/library.deps @@ -1 +1 @@ -depends=Adafruit ILI9341, Adafruit BusIO, SD, Adafruit NeoPixel, Adafruit VS1053 Library, Adafruit BluefruitLE nRF51, Adafruit seesaw Library, Ethernet, Stepper, Adafruit IO Arduino, FastLED, Adafruit LiquidCrystal, Adafruit SoftServo, TinyWireM, Adafruit AM radio library, WaveHC, Adafruit LED Backpack Library, MAX31850 OneWire, Adafruit VC0706 Serial Camera Library, RTClib, Adafruit SleepyDog Library, Adafruit Thermal Printer Library, Adafruit Zero I2S Library, Adafruit EPD, Adafruit SSD1351 library, Adafruit FONA Library, Adafruit Motor Shield V2 Library, Adafruit NeoMatrix, Adafruit Soundboard library, Adafruit Circuit Playground, ArduinoJson, Adafruit TCS34725, Adafruit Pixie, Adafruit GPS Library, TinyGPS, WiFi101, Adafruit DotStar, Adafruit Si7021 Library, Adafruit WS2801 Library, Mouse, Keyboard, Time, IRremote, Adafruit LSM9DS0 Library, Adafruit Arcada Library, MIDIUSB, PubSubClient, Adafruit LIS2MDL, Adafruit NeoPXL8, Adafruit MCP23017 Arduino Library, Adafruit MLX90640, LiquidCrystal, Adafruit NeoTrellis M4 Library, RGB matrix Panel, Adafruit MLX90614 Library, Adafruit RGB LCD Shield Library, MAX6675 library, Adafruit MP3, Adafruit Keypad, Adafruit Arcada GifDecoder, Keypad, Neosegment, Encoder, Adafruit TiCoServo, Adafruit Trellis Library, FauxmoESP, Adafruit LSM303 Accel, Adafruit LSM303DLH Mag, Adafruit LSM303DLHC, CapacitiveSensor, Adafruit Zero PDM Library, Adafruit DMA neopixel library, elapsedMillis, DST RTC, Adafruit SHARP Memory Display, Adafruit SPIFlash, BSEC Software Library, WiiChuck, Adafruit DPS310, Adafruit AHTX0, RotaryEncoder, Adafruit MCP9808 Library, LSM303, Adafruit Protomatter, Adafruit IS31FL3741 Library, Sensirion I2C SCD4x, Adafruit TestBed, Bounce2, Adafruit AHRS, Adafruit DRV2605 Library, STM32duino VL53L4CD, PicoDVI - Adafruit Fork, Adafruit MMA8451 Library, Adafruit TSC2007, GFX Library for Arduino, Adafruit PyCamera Library, Adafruit ADG72x, Adafruit BNO055, Adafruit SHT4x Library, Adafruit VCNL4200 Library, Adafruit GC9A01A, Adafruit DVI HSTX, Adafruit TLV320 I2S +depends=Adafruit SSD1305, Adafruit ILI9341, Adafruit BusIO, SD, Adafruit NeoPixel, Adafruit VS1053 Library, Adafruit BluefruitLE nRF51, Adafruit seesaw Library, Ethernet, Stepper, Adafruit IO Arduino, FastLED, Adafruit LiquidCrystal, Adafruit SoftServo, TinyWireM, Adafruit AM radio library, WaveHC, Adafruit LED Backpack Library, MAX31850 OneWire, Adafruit VC0706 Serial Camera Library, RTClib, Adafruit SleepyDog Library, Adafruit Thermal Printer Library, Adafruit Zero I2S Library, Adafruit EPD, Adafruit SSD1351 library, Adafruit FONA Library, Adafruit Motor Shield V2 Library, Adafruit NeoMatrix, Adafruit Soundboard library, Adafruit Circuit Playground, ArduinoJson, Adafruit TCS34725, Adafruit Pixie, Adafruit GPS Library, TinyGPS, WiFi101, Adafruit DotStar, Adafruit Si7021 Library, Adafruit WS2801 Library, Mouse, Keyboard, Time, IRremote, Adafruit LSM9DS0 Library, Adafruit Arcada Library, MIDIUSB, PubSubClient, Adafruit LIS2MDL, Adafruit NeoPXL8, Adafruit MCP23017 Arduino Library, Adafruit MLX90640, LiquidCrystal, Adafruit NeoTrellis M4 Library, RGB matrix Panel, Adafruit MLX90614 Library, Adafruit RGB LCD Shield Library, MAX6675 library, Adafruit MP3, Adafruit Keypad, Adafruit Arcada GifDecoder, Keypad, Neosegment, Encoder, Adafruit TiCoServo, Adafruit Trellis Library, FauxmoESP, Adafruit LSM303 Accel, Adafruit LSM303DLH Mag, Adafruit LSM303DLHC, CapacitiveSensor, Adafruit Zero PDM Library, Adafruit DMA neopixel library, elapsedMillis, DST RTC, Adafruit SHARP Memory Display, Adafruit SPIFlash, BSEC Software Library, WiiChuck, Adafruit DPS310, Adafruit AHTX0, RotaryEncoder, Adafruit MCP9808 Library, LSM303, Adafruit Protomatter, Adafruit IS31FL3741 Library, Sensirion I2C SCD4x, Adafruit TestBed, Bounce2, Adafruit AHRS, Adafruit DRV2605 Library, STM32duino VL53L4CD, PicoDVI - Adafruit Fork, Adafruit MMA8451 Library, Adafruit TSC2007, GFX Library for Arduino, Adafruit PyCamera Library, Adafruit ADG72x, Adafruit BNO055, Adafruit SHT4x Library, Adafruit VCNL4200 Library, Adafruit GC9A01A, Adafruit DVI HSTX, Adafruit TLV320 I2S