diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:01:31 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:01:31 +0000 |
commit | 4795ba337f7dba15ceb94f8884a1b15ad903b746 (patch) | |
tree | 9de2e37332c9056aacfebc24339302109cf2c64a | |
parent | c8df0ff0cbe131029d794ad3b5a9d8d0cd0de645 (diff) | |
parent | 763d76ecdc285e49c0535f256b0df4a932e1f145 (diff) | |
download | bumble-4795ba337f7dba15ceb94f8884a1b15ad903b746.tar.gz |
Snap for 10447354 from 763d76ecdc285e49c0535f256b0df4a932e1f145 to mainline-cellbroadcast-releaseaml_cbr_340914000
Change-Id: I0e4f56b50202635e074235bc417718406219e673
229 files changed, 25784 insertions, 7258 deletions
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..be480be --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Migrate code style to Black +135df0dcc01ab765f432e19b1a5202d29bd55545 diff --git a/.github/workflows/code-check.yml b/.github/workflows/code-check.yml new file mode 100644 index 0000000..b6cf8fd --- /dev/null +++ b/.github/workflows/code-check.yml @@ -0,0 +1,35 @@ +# Check the code against the formatter and linter +name: Code format and lint check + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +permissions: + contents: read + +jobs: + check: + name: Check Code + runs-on: ubuntu-latest + + steps: + - name: Check out from Git + uses: actions/checkout@v3 + - name: Get history and tags for SCM versioning to work + run: | + git fetch --prune --unshallow + git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install ".[build,test,development]" + - name: Check + run: | + invoke project.pre-commit diff --git a/.github/workflows/python-build-test.yml b/.github/workflows/python-build-test.yml index 062f153..72c7b43 100644 --- a/.github/workflows/python-build-test.yml +++ b/.github/workflows/python-build-test.yml @@ -14,21 +14,30 @@ jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10"] + fail-fast: false steps: - - uses: actions/checkout@v3 - - name: Set up Python 3.10 - uses: actions/setup-python@v3 + - name: Check out from Git + uses: actions/checkout@v3 + - name: Get history and tags for SCM versioning to work + run: | + git fetch --prune --unshallow + git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install ".[test,development,documentation]" - - name: Test with pytest + python -m pip install ".[build,test,development,documentation]" + - name: Test run: | - pytest + invoke test - name: Build run: | inv build - inv mkdocs + inv build.mkdocs diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml new file mode 100644 index 0000000..7d9bc19 --- /dev/null +++ b/.github/workflows/python-publish.yml @@ -0,0 +1,37 @@ +name: Upload Python Package + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + deploy: + name: Build and publish Python ðŸ distributions 📦 to PyPI and TestPyPI + runs-on: ubuntu-latest + + steps: + - name: Check out from Git + uses: actions/checkout@v3 + - name: Get history and tags for SCM versioning to work + run: | + git fetch --prune --unshallow + git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install build + - name: Build package + run: python -m build + - name: Publish package to PyPI + if: github.event_name == 'release' && startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} @@ -3,9 +3,9 @@ build/ dist/ *.egg-info/ *~ -bumble/__pycache__ docs/mkdocs/site -tests/__pycache__ test-results.xml -bumble/transport/__pycache__ -bumble/profiles/__pycache__ +__pycache__ +# generated by setuptools_scm +bumble/_version.py +.vscode/launch.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..864fe69 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,80 @@ +{ + "cSpell.words": [ + "Abortable", + "altsetting", + "ansiblue", + "ansicyan", + "ansigreen", + "ansimagenta", + "ansired", + "ansiyellow", + "appendleft", + "ASHA", + "asyncio", + "ATRAC", + "avdtp", + "bitpool", + "bitstruct", + "BSCP", + "BTPROTO", + "CCCD", + "cccds", + "cmac", + "CONNECTIONLESS", + "csrcs", + "datagram", + "DATALINK", + "delayreport", + "deregisters", + "deregistration", + "dhkey", + "diversifier", + "Fitbit", + "GATTLINK", + "HANDSFREE", + "keydown", + "keyup", + "levelname", + "libc", + "libusb", + "MITM", + "NDIS", + "NONBLOCK", + "NONCONN", + "OXIMETER", + "popleft", + "psms", + "pyee", + "pyusb", + "rfcomm", + "ROHC", + "rssi", + "SEID", + "seids", + "SERV", + "ssrc", + "strerror", + "subband", + "subbands", + "subevent", + "Subrating", + "substates", + "tobytes", + "tsep", + "usbmodem", + "vhci", + "websockets", + "xcursor", + "ycursor" + ], + "[python]": { + "editor.rulers": [88] + }, + "python.formatting.provider": "black", + "pylint.importStrategy": "useBundled", + "python.testing.pytestArgs": [ + "." + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} @@ -23,6 +23,41 @@ python_library_host { "bumble/transport/*.py", ], libs: [ - "py-cryptography", + "pyee", + ] +} + +python_library_host { + name: "bumble-pandora", + srcs: [ + "bumble/pandora/*.py", + ], + libs: [ + "bumble", + "pandora-python", + "libprotobuf-python", + ], + data: [ + "bumble/pandora/py.typed" + ] +} + +python_test_host { + name: "bumble_pandora_server", + main: "apps/pandora_server.py", + srcs: [ + "apps/pandora_server.py", ], + libs: [ + "bumble-pandora", + "pandora-python", + ], + + test_options: { + unit_test: false, + }, + + test_suites: [ + "general-tests" + ] } @@ -199,4 +199,23 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License.
\ No newline at end of file + limitations under the License. + + +--- + + +Files: bumble/colors.py +Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. @@ -1,3 +1,5 @@ boccongibod@google.com +charliebout@google.com girardier@google.com licorne@google.com +uael@google.com @@ -9,18 +9,49 @@ Bluetooth Stack for Apps, Emulation, Test and Experimentation ============================================================= -<img src="docs/mkdocs/src/images/logo_framed.png" alt="drawing" width="200" height="200"/> +<img src="docs/mkdocs/src/images/logo_framed.png" alt="Logo" width="200" height="200"/> Bumble is a full-featured Bluetooth stack written entirely in Python. It supports most of the common Bluetooth Low Energy (BLE) and Bluetooth Classic (BR/EDR) protocols and profiles, including GAP, L2CAP, ATT, GATT, SMP, SDP, RFCOMM, HFP, HID and A2DP. The stack can be used with physical radios via HCI over USB, UART, or the Linux VHCI, as well as virtual radios, including the virtual Bluetooth support of the Android emulator. ## Documentation -Browse the pre-built [Online Documentation](https://google.github.io/bumble/), +Browse the pre-built [Online Documentation](https://google.github.io/bumble/), or see the documentation source under `docs/mkdocs/src`, or build the static HTML site from the markdown text with: ``` -mkdocs build -f docs/mkdocs/mkdocs.yml +mkdocs build -f docs/mkdocs/mkdocs.yml ``` +## Usage + +### Getting Started + +For a quick start to using Bumble, see the [Getting Started](docs/mkdocs/src/getting_started.md) guide. + +### Dependencies + +To install package dependencies needed to run the bumble examples, execute the following commands: + +``` +python -m pip install --upgrade pip +python -m pip install ".[test,development,documentation]" +``` + +### Examples + +Refer to the [Examples Documentation](examples/README.md) for details on the included example scripts and how to run them. + +The complete [list of Examples](/docs/mkdocs/src/examples/index.md), and what they are designed to do is here. + +There are also a set of [Apps and Tools](docs/mkdocs/src/apps_and_tools/index.md) that show the utility of Bumble. + +### Using Bumble With a USB Dongle + +Bumble is easiest to use with a dedicated USB dongle. +This is because internal Bluetooth interfaces tend to be locked down by the operating system. +You can use the [usb_probe](/docs/mkdocs/src/apps_and_tools/usb_probe.md) tool (all platforms) or `lsusb` (Linux or macOS) to list the available USB devices on your system. + +See the [USB Transport](/docs/mkdocs/src/transports/usb.md) page for details on how to refer to USB devices. Also, if your are on a mac, see [these instructions](docs/mkdocs/src/platforms/macos.md). + ## License Licensed under the [Apache 2.0](LICENSE) License. diff --git a/apps/README.md b/apps/README.md index 0abcf09..814d23d 100644 --- a/apps/README.md +++ b/apps/README.md @@ -47,5 +47,3 @@ NOTE: this assumes you're running a Link Relay on port `10723`. ## `console.py` A simple text-based-ui interactive Bluetooth device with GATT client capabilities. - - diff --git a/apps/bench.py b/apps/bench.py new file mode 100644 index 0000000..4708403 --- /dev/null +++ b/apps/bench.py @@ -0,0 +1,1209 @@ +# Copyright 2021-2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import asyncio +import enum +import logging +import os +import struct +import time + +import click + +from bumble.core import ( + BT_BR_EDR_TRANSPORT, + BT_LE_TRANSPORT, + BT_L2CAP_PROTOCOL_ID, + BT_RFCOMM_PROTOCOL_ID, + UUID, + CommandTimeoutError, +) +from bumble.colors import color +from bumble.device import Connection, ConnectionParametersPreferences, Device, Peer +from bumble.gatt import Characteristic, CharacteristicValue, Service +from bumble.hci import ( + HCI_LE_1M_PHY, + HCI_LE_2M_PHY, + HCI_LE_CODED_PHY, + HCI_Constant, + HCI_Error, + HCI_StatusError, +) +from bumble.sdp import ( + SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, + SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, + SDP_PUBLIC_BROWSE_ROOT, + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, + DataElement, + ServiceAttribute, +) +from bumble.transport import open_transport_or_link +import bumble.rfcomm +import bumble.core +from bumble.utils import AsyncRunner + + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- +logger = logging.getLogger(__name__) + + +# ----------------------------------------------------------------------------- +# Constants +# ----------------------------------------------------------------------------- +DEFAULT_CENTRAL_ADDRESS = 'F0:F0:F0:F0:F0:F0' +DEFAULT_CENTRAL_NAME = 'Speed Central' +DEFAULT_PERIPHERAL_ADDRESS = 'F1:F1:F1:F1:F1:F1' +DEFAULT_PERIPHERAL_NAME = 'Speed Peripheral' + +SPEED_SERVICE_UUID = '50DB505C-8AC4-4738-8448-3B1D9CC09CC5' +SPEED_TX_UUID = 'E789C754-41A1-45F4-A948-A0A1A90DBA53' +SPEED_RX_UUID = '016A2CC7-E14B-4819-935F-1F56EAE4098D' + +DEFAULT_L2CAP_PSM = 1234 +DEFAULT_L2CAP_MAX_CREDITS = 128 +DEFAULT_L2CAP_MTU = 1022 +DEFAULT_L2CAP_MPS = 1024 + +DEFAULT_LINGER_TIME = 1.0 + +DEFAULT_RFCOMM_CHANNEL = 8 + +# ----------------------------------------------------------------------------- +# Utils +# ----------------------------------------------------------------------------- +def parse_packet(packet): + if len(packet) < 1: + print( + color(f'!!! Packet too short (got {len(packet)} bytes, need >= 1)', 'red') + ) + raise ValueError('packet too short') + + try: + packet_type = PacketType(packet[0]) + except ValueError: + print(color(f'!!! Invalid packet type 0x{packet[0]:02X}', 'red')) + raise + + return (packet_type, packet[1:]) + + +def parse_packet_sequence(packet_data): + if len(packet_data) < 5: + print( + color( + f'!!!Packet too short (got {len(packet_data)} bytes, need >= 5)', + 'red', + ) + ) + raise ValueError('packet too short') + return struct.unpack_from('>bI', packet_data, 0) + + +def le_phy_name(phy_id): + return {HCI_LE_1M_PHY: '1M', HCI_LE_2M_PHY: '2M', HCI_LE_CODED_PHY: 'CODED'}.get( + phy_id, HCI_Constant.le_phy_name(phy_id) + ) + + +def print_connection(connection): + if connection.transport == BT_LE_TRANSPORT: + phy_state = ( + 'PHY=' + f'RX:{le_phy_name(connection.phy.rx_phy)}/' + f'TX:{le_phy_name(connection.phy.tx_phy)}' + ) + + data_length = f'DL={connection.data_length}' + connection_parameters = ( + 'Parameters=' + f'{connection.parameters.connection_interval * 1.25:.2f}/' + f'{connection.parameters.peripheral_latency}/' + f'{connection.parameters.supervision_timeout * 10} ' + ) + + else: + phy_state = '' + data_length = '' + connection_parameters = '' + + mtu = connection.att_mtu + + print( + f'{color("@@@ Connection:", "yellow")} ' + f'{connection_parameters} ' + f'{data_length} ' + f'{phy_state} ' + f'MTU={mtu}' + ) + + +def make_sdp_records(channel): + return { + 0x00010001: [ + ServiceAttribute( + SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, + DataElement.unsigned_integer_32(0x00010001), + ), + ServiceAttribute( + SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, + DataElement.sequence([DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)]), + ), + ServiceAttribute( + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [DataElement.uuid(UUID('E6D55659-C8B4-4B85-96BB-B1143AF6D3AE'))] + ), + ), + ServiceAttribute( + SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [ + DataElement.sequence([DataElement.uuid(BT_L2CAP_PROTOCOL_ID)]), + DataElement.sequence( + [ + DataElement.uuid(BT_RFCOMM_PROTOCOL_ID), + DataElement.unsigned_integer_8(channel), + ] + ), + ] + ), + ), + ] + } + + +class PacketType(enum.IntEnum): + RESET = 0 + SEQUENCE = 1 + ACK = 2 + + +PACKET_FLAG_LAST = 1 + +# ----------------------------------------------------------------------------- +# Sender +# ----------------------------------------------------------------------------- +class Sender: + def __init__(self, packet_io, start_delay, packet_size, packet_count): + self.tx_start_delay = start_delay + self.tx_packet_size = packet_size + self.tx_packet_count = packet_count + self.packet_io = packet_io + self.packet_io.packet_listener = self + self.start_time = 0 + self.bytes_sent = 0 + self.done = asyncio.Event() + + def reset(self): + pass + + async def run(self): + print(color('--- Waiting for I/O to be ready...', 'blue')) + await self.packet_io.ready.wait() + print(color('--- Go!', 'blue')) + + if self.tx_start_delay: + print(color(f'*** Startup delay: {self.tx_start_delay}', 'blue')) + await asyncio.sleep(self.tx_start_delay) # FIXME + + print(color('=== Sending RESET', 'magenta')) + await self.packet_io.send_packet(bytes([PacketType.RESET])) + self.start_time = time.time() + for tx_i in range(self.tx_packet_count): + packet_flags = PACKET_FLAG_LAST if tx_i == self.tx_packet_count - 1 else 0 + packet = struct.pack( + '>bbI', + PacketType.SEQUENCE, + packet_flags, + tx_i, + ) + bytes(self.tx_packet_size - 6) + print(color(f'Sending packet {tx_i}: {len(packet)} bytes', 'yellow')) + self.bytes_sent += len(packet) + await self.packet_io.send_packet(packet) + + await self.done.wait() + print(color('=== Done!', 'magenta')) + + def on_packet_received(self, packet): + try: + packet_type, _ = parse_packet(packet) + except ValueError: + return + + if packet_type == PacketType.ACK: + elapsed = time.time() - self.start_time + average_tx_speed = self.bytes_sent / elapsed + print( + color( + f'@@@ Received ACK. Speed: average={average_tx_speed:.4f}' + f' ({self.bytes_sent} bytes in {elapsed:.2f} seconds)', + 'green', + ) + ) + self.done.set() + + +# ----------------------------------------------------------------------------- +# Receiver +# ----------------------------------------------------------------------------- +class Receiver: + def __init__(self, packet_io): + self.reset() + self.packet_io = packet_io + self.packet_io.packet_listener = self + self.done = asyncio.Event() + + def reset(self): + self.expected_packet_index = 0 + self.start_timestamp = 0.0 + self.last_timestamp = 0.0 + self.bytes_received = 0 + + def on_packet_received(self, packet): + try: + packet_type, packet_data = parse_packet(packet) + except ValueError: + return + + now = time.time() + + if packet_type == PacketType.RESET: + print(color('=== Received RESET', 'magenta')) + self.reset() + self.start_timestamp = now + return + + try: + packet_flags, packet_index = parse_packet_sequence(packet_data) + except ValueError: + return + print( + f'<<< Received packet {packet_index}: ' + f'flags=0x{packet_flags:02X}, {len(packet)} bytes' + ) + + if packet_index != self.expected_packet_index: + print( + color( + f'!!! Unexpected packet, expected {self.expected_packet_index} ' + f'but received {packet_index}' + ) + ) + + elapsed_since_start = now - self.start_timestamp + elapsed_since_last = now - self.last_timestamp + self.bytes_received += len(packet) + instant_rx_speed = len(packet) / elapsed_since_last + average_rx_speed = self.bytes_received / elapsed_since_start + print( + color( + f'Speed: instant={instant_rx_speed:.4f}, average={average_rx_speed:.4f}', + 'yellow', + ) + ) + + self.last_timestamp = now + self.expected_packet_index = packet_index + 1 + + if packet_flags & PACKET_FLAG_LAST: + AsyncRunner.spawn( + self.packet_io.send_packet( + struct.pack('>bbI', PacketType.ACK, packet_flags, packet_index) + ) + ) + print(color('@@@ Received last packet', 'green')) + self.done.set() + + async def run(self): + await self.done.wait() + print(color('=== Done!', 'magenta')) + + +# ----------------------------------------------------------------------------- +# Ping +# ----------------------------------------------------------------------------- +class Ping: + def __init__(self, packet_io, start_delay, packet_size, packet_count): + self.tx_start_delay = start_delay + self.tx_packet_size = packet_size + self.tx_packet_count = packet_count + self.packet_io = packet_io + self.packet_io.packet_listener = self + self.done = asyncio.Event() + self.current_packet_index = 0 + self.ping_sent_time = 0.0 + self.latencies = [] + + def reset(self): + pass + + async def run(self): + print(color('--- Waiting for I/O to be ready...', 'blue')) + await self.packet_io.ready.wait() + print(color('--- Go!', 'blue')) + + if self.tx_start_delay: + print(color(f'*** Startup delay: {self.tx_start_delay}', 'blue')) + await asyncio.sleep(self.tx_start_delay) # FIXME + + print(color('=== Sending RESET', 'magenta')) + await self.packet_io.send_packet(bytes([PacketType.RESET])) + + await self.send_next_ping() + + await self.done.wait() + average_latency = sum(self.latencies) / len(self.latencies) + print(color(f'@@@ Average latency: {average_latency:.2f}')) + print(color('=== Done!', 'magenta')) + + async def send_next_ping(self): + packet = struct.pack( + '>bbI', + PacketType.SEQUENCE, + PACKET_FLAG_LAST + if self.current_packet_index == self.tx_packet_count - 1 + else 0, + self.current_packet_index, + ) + bytes(self.tx_packet_size - 6) + print(color(f'Sending packet {self.current_packet_index}', 'yellow')) + self.ping_sent_time = time.time() + await self.packet_io.send_packet(packet) + + def on_packet_received(self, packet): + elapsed = time.time() - self.ping_sent_time + + try: + packet_type, packet_data = parse_packet(packet) + except ValueError: + return + + try: + packet_flags, packet_index = parse_packet_sequence(packet_data) + except ValueError: + return + + if packet_type == PacketType.ACK: + latency = elapsed * 1000 + self.latencies.append(latency) + print( + color( + f'<<< Received ACK [{packet_index}], latency={latency:.2f}ms', + 'green', + ) + ) + + if packet_index == self.current_packet_index: + self.current_packet_index += 1 + else: + print( + color( + f'!!! Unexpected packet, expected {self.current_packet_index} ' + f'but received {packet_index}' + ) + ) + + if packet_flags & PACKET_FLAG_LAST: + self.done.set() + return + + AsyncRunner.spawn(self.send_next_ping()) + + +# ----------------------------------------------------------------------------- +# Pong +# ----------------------------------------------------------------------------- +class Pong: + def __init__(self, packet_io): + self.reset() + self.packet_io = packet_io + self.packet_io.packet_listener = self + self.done = asyncio.Event() + + def reset(self): + self.expected_packet_index = 0 + + def on_packet_received(self, packet): + try: + packet_type, packet_data = parse_packet(packet) + except ValueError: + return + + if packet_type == PacketType.RESET: + print(color('=== Received RESET', 'magenta')) + self.reset() + return + + try: + packet_flags, packet_index = parse_packet_sequence(packet_data) + except ValueError: + return + print( + color( + f'<<< Received packet {packet_index}: ' + f'flags=0x{packet_flags:02X}, {len(packet)} bytes', + 'green', + ) + ) + + if packet_index != self.expected_packet_index: + print( + color( + f'!!! Unexpected packet, expected {self.expected_packet_index} ' + f'but received {packet_index}' + ) + ) + + self.expected_packet_index = packet_index + 1 + + AsyncRunner.spawn( + self.packet_io.send_packet( + struct.pack('>bbI', PacketType.ACK, packet_flags, packet_index) + ) + ) + + if packet_flags & PACKET_FLAG_LAST: + self.done.set() + + async def run(self): + await self.done.wait() + print(color('=== Done!', 'magenta')) + + +# ----------------------------------------------------------------------------- +# GattClient +# ----------------------------------------------------------------------------- +class GattClient: + def __init__(self, _device, att_mtu=None): + self.att_mtu = att_mtu + self.speed_rx = None + self.speed_tx = None + self.packet_listener = None + self.ready = asyncio.Event() + + async def on_connection(self, connection): + peer = Peer(connection) + + if self.att_mtu: + print(color(f'*** Requesting MTU update: {self.att_mtu}', 'blue')) + await peer.request_mtu(self.att_mtu) + + print(color('*** Discovering services...', 'blue')) + await peer.discover_services() + + speed_services = peer.get_services_by_uuid(SPEED_SERVICE_UUID) + if not speed_services: + print(color('!!! Speed Service not found', 'red')) + return + speed_service = speed_services[0] + print(color('*** Discovering characteristics...', 'blue')) + await speed_service.discover_characteristics() + + speed_txs = speed_service.get_characteristics_by_uuid(SPEED_TX_UUID) + if not speed_txs: + print(color('!!! Speed TX not found', 'red')) + return + self.speed_tx = speed_txs[0] + + speed_rxs = speed_service.get_characteristics_by_uuid(SPEED_RX_UUID) + if not speed_rxs: + print(color('!!! Speed RX not found', 'red')) + return + self.speed_rx = speed_rxs[0] + + print(color('*** Subscribing to RX', 'blue')) + await self.speed_rx.subscribe(self.on_packet_received) + + print(color('*** Discovery complete', 'blue')) + + connection.on('disconnection', self.on_disconnection) + self.ready.set() + + def on_disconnection(self, _): + self.ready.clear() + + def on_packet_received(self, packet): + if self.packet_listener: + self.packet_listener.on_packet_received(packet) + + async def send_packet(self, packet): + await self.speed_tx.write_value(packet) + + +# ----------------------------------------------------------------------------- +# GattServer +# ----------------------------------------------------------------------------- +class GattServer: + def __init__(self, device): + self.device = device + self.packet_listener = None + self.ready = asyncio.Event() + + # Setup the GATT service + self.speed_tx = Characteristic( + SPEED_TX_UUID, + Characteristic.Properties.WRITE, + Characteristic.WRITEABLE, + CharacteristicValue(write=self.on_tx_write), + ) + self.speed_rx = Characteristic( + SPEED_RX_UUID, Characteristic.Properties.NOTIFY, 0 + ) + + speed_service = Service( + SPEED_SERVICE_UUID, + [self.speed_tx, self.speed_rx], + ) + device.add_services([speed_service]) + + self.speed_rx.on('subscription', self.on_rx_subscription) + + async def on_connection(self, connection): + connection.on('disconnection', self.on_disconnection) + + def on_disconnection(self, _): + self.ready.clear() + + def on_rx_subscription(self, _connection, notify_enabled, _indicate_enabled): + if notify_enabled: + print(color('*** RX subscription', 'blue')) + self.ready.set() + else: + print(color('*** RX un-subscription', 'blue')) + self.ready.clear() + + def on_tx_write(self, _, value): + if self.packet_listener: + self.packet_listener.on_packet_received(value) + + async def send_packet(self, packet): + await self.device.notify_subscribers(self.speed_rx, packet) + + +# ----------------------------------------------------------------------------- +# StreamedPacketIO +# ----------------------------------------------------------------------------- +class StreamedPacketIO: + def __init__(self): + self.packet_listener = None + self.io_sink = None + self.rx_packet = b'' + self.rx_packet_header = b'' + self.rx_packet_need = 0 + + def on_packet(self, packet): + while packet: + if self.rx_packet_need: + chunk = packet[: self.rx_packet_need] + self.rx_packet += chunk + packet = packet[len(chunk) :] + self.rx_packet_need -= len(chunk) + if not self.rx_packet_need: + # Packet completed + if self.packet_listener: + self.packet_listener.on_packet_received(self.rx_packet) + + self.rx_packet = b'' + self.rx_packet_header = b'' + else: + # Expect the next packet + header_bytes_needed = 2 - len(self.rx_packet_header) + header_bytes = packet[:header_bytes_needed] + self.rx_packet_header += header_bytes + if len(self.rx_packet_header) != 2: + return + packet = packet[len(header_bytes) :] + self.rx_packet_need = struct.unpack('>H', self.rx_packet_header)[0] + + async def send_packet(self, packet): + if not self.io_sink: + print(color('!!! No sink, dropping packet', 'red')) + return + + # pylint: disable-next=not-callable + self.io_sink(struct.pack('>H', len(packet)) + packet) + + +# ----------------------------------------------------------------------------- +# L2capClient +# ----------------------------------------------------------------------------- +class L2capClient(StreamedPacketIO): + def __init__( + self, + _device, + psm=DEFAULT_L2CAP_PSM, + max_credits=DEFAULT_L2CAP_MAX_CREDITS, + mtu=DEFAULT_L2CAP_MTU, + mps=DEFAULT_L2CAP_MPS, + ): + super().__init__() + self.psm = psm + self.max_credits = max_credits + self.mtu = mtu + self.mps = mps + self.ready = asyncio.Event() + + async def on_connection(self, connection): + connection.on('disconnection', self.on_disconnection) + + # Connect a new L2CAP channel + print(color(f'>>> Opening L2CAP channel on PSM = {self.psm}', 'yellow')) + try: + l2cap_channel = await connection.open_l2cap_channel( + psm=self.psm, + max_credits=self.max_credits, + mtu=self.mtu, + mps=self.mps, + ) + print(color('*** L2CAP channel:', 'cyan'), l2cap_channel) + except Exception as error: + print(color(f'!!! Connection failed: {error}', 'red')) + return + + l2cap_channel.sink = self.on_packet + l2cap_channel.on('close', self.on_l2cap_close) + self.io_sink = l2cap_channel.write + + self.ready.set() + + def on_disconnection(self, _): + pass + + def on_l2cap_close(self): + print(color('*** L2CAP channel closed', 'red')) + + +# ----------------------------------------------------------------------------- +# L2capServer +# ----------------------------------------------------------------------------- +class L2capServer(StreamedPacketIO): + def __init__( + self, + device, + psm=DEFAULT_L2CAP_PSM, + max_credits=DEFAULT_L2CAP_MAX_CREDITS, + mtu=DEFAULT_L2CAP_MTU, + mps=DEFAULT_L2CAP_MPS, + ): + super().__init__() + self.l2cap_channel = None + self.ready = asyncio.Event() + + # Listen for incoming L2CAP CoC connections + device.register_l2cap_channel_server( + psm=psm, + server=self.on_l2cap_channel, + max_credits=max_credits, + mtu=mtu, + mps=mps, + ) + print(color(f'### Listening for CoC connection on PSM {psm}', 'yellow')) + + async def on_connection(self, connection): + connection.on('disconnection', self.on_disconnection) + + def on_disconnection(self, _): + pass + + def on_l2cap_channel(self, l2cap_channel): + print(color('*** L2CAP channel:', 'cyan'), l2cap_channel) + + self.io_sink = l2cap_channel.write + l2cap_channel.on('close', self.on_l2cap_close) + l2cap_channel.sink = self.on_packet + + self.ready.set() + + def on_l2cap_close(self): + print(color('*** L2CAP channel closed', 'red')) + self.l2cap_channel = None + + +# ----------------------------------------------------------------------------- +# RfcommClient +# ----------------------------------------------------------------------------- +class RfcommClient(StreamedPacketIO): + def __init__(self, device): + super().__init__() + self.device = device + self.ready = asyncio.Event() + + async def on_connection(self, connection): + connection.on('disconnection', self.on_disconnection) + + # Create a client and start it + print(color('*** Starting RFCOMM client...', 'blue')) + rfcomm_client = bumble.rfcomm.Client(self.device, connection) + rfcomm_mux = await rfcomm_client.start() + print(color('*** Started', 'blue')) + + channel = DEFAULT_RFCOMM_CHANNEL + print(color(f'### Opening session for channel {channel}...', 'yellow')) + try: + rfcomm_session = await rfcomm_mux.open_dlc(channel) + print(color('### Session open', 'yellow'), rfcomm_session) + except bumble.core.ConnectionError as error: + print(color(f'!!! Session open failed: {error}', 'red')) + await rfcomm_mux.disconnect() + return + + rfcomm_session.sink = self.on_packet + self.io_sink = rfcomm_session.write + + self.ready.set() + + def on_disconnection(self, _): + pass + + +# ----------------------------------------------------------------------------- +# RfcommServer +# ----------------------------------------------------------------------------- +class RfcommServer(StreamedPacketIO): + def __init__(self, device): + super().__init__() + self.ready = asyncio.Event() + + # Create and register a server + rfcomm_server = bumble.rfcomm.Server(device) + + # Listen for incoming DLC connections + channel_number = rfcomm_server.listen(self.on_dlc, DEFAULT_RFCOMM_CHANNEL) + + # Setup the SDP to advertise this channel + device.sdp_service_records = make_sdp_records(channel_number) + + print( + color( + f'### Listening for RFComm connection on channel {channel_number}', + 'yellow', + ) + ) + + async def on_connection(self, connection): + connection.on('disconnection', self.on_disconnection) + + def on_disconnection(self, _): + pass + + def on_dlc(self, dlc): + print(color('*** DLC connected:', 'blue'), dlc) + dlc.sink = self.on_packet + self.io_sink = dlc.write + + +# ----------------------------------------------------------------------------- +# Central +# ----------------------------------------------------------------------------- +class Central(Connection.Listener): + def __init__( + self, + transport, + peripheral_address, + classic, + role_factory, + mode_factory, + connection_interval, + phy, + ): + super().__init__() + self.transport = transport + self.peripheral_address = peripheral_address + self.classic = classic + self.role_factory = role_factory + self.mode_factory = mode_factory + self.device = None + self.connection = None + + if phy: + self.phy = { + '1m': HCI_LE_1M_PHY, + '2m': HCI_LE_2M_PHY, + 'coded': HCI_LE_CODED_PHY, + }[phy] + else: + self.phy = None + + if connection_interval: + connection_parameter_preferences = ConnectionParametersPreferences() + connection_parameter_preferences.connection_interval_min = ( + connection_interval + ) + connection_parameter_preferences.connection_interval_max = ( + connection_interval + ) + + # Preferences for the 1M PHY are always set. + self.connection_parameter_preferences = { + HCI_LE_1M_PHY: connection_parameter_preferences, + } + + if self.phy not in (None, HCI_LE_1M_PHY): + # Add an connections parameters entry for this PHY. + self.connection_parameter_preferences[ + self.phy + ] = connection_parameter_preferences + else: + self.connection_parameter_preferences = None + + async def run(self): + print(color('>>> Connecting to HCI...', 'green')) + async with await open_transport_or_link(self.transport) as ( + hci_source, + hci_sink, + ): + print(color('>>> Connected', 'green')) + + central_address = DEFAULT_CENTRAL_ADDRESS + self.device = Device.with_hci( + DEFAULT_CENTRAL_NAME, central_address, hci_source, hci_sink + ) + mode = self.mode_factory(self.device) + role = self.role_factory(mode) + self.device.classic_enabled = self.classic + + await self.device.power_on() + + print(color(f'### Connecting to {self.peripheral_address}...', 'cyan')) + try: + self.connection = await self.device.connect( + self.peripheral_address, + connection_parameters_preferences=self.connection_parameter_preferences, + transport=BT_BR_EDR_TRANSPORT if self.classic else BT_LE_TRANSPORT, + ) + except CommandTimeoutError: + print(color('!!! Connection timed out', 'red')) + return + except bumble.core.ConnectionError as error: + print(color(f'!!! Connection error: {error}', 'red')) + return + except HCI_StatusError as error: + print(color(f'!!! Connection failed: {error.error_name}')) + return + print(color('### Connected', 'cyan')) + self.connection.listener = self + print_connection(self.connection) + + await mode.on_connection(self.connection) + + # Set the PHY if requested + if self.phy is not None: + try: + await self.connection.set_phy( + tx_phys=[self.phy], rx_phys=[self.phy] + ) + except HCI_Error as error: + print( + color( + f'!!! Unable to set the PHY: {error.error_name}', 'yellow' + ) + ) + + await role.run() + await asyncio.sleep(DEFAULT_LINGER_TIME) + + def on_disconnection(self, reason): + print(color(f'!!! Disconnection: reason={reason}', 'red')) + self.connection = None + + def on_connection_parameters_update(self): + print_connection(self.connection) + + def on_connection_phy_update(self): + print_connection(self.connection) + + def on_connection_att_mtu_update(self): + print_connection(self.connection) + + def on_connection_data_length_change(self): + print_connection(self.connection) + + +# ----------------------------------------------------------------------------- +# Peripheral +# ----------------------------------------------------------------------------- +class Peripheral(Device.Listener, Connection.Listener): + def __init__(self, transport, classic, role_factory, mode_factory): + self.transport = transport + self.classic = classic + self.role_factory = role_factory + self.role = None + self.mode_factory = mode_factory + self.mode = None + self.device = None + self.connection = None + self.connected = asyncio.Event() + + async def run(self): + print(color('>>> Connecting to HCI...', 'green')) + async with await open_transport_or_link(self.transport) as ( + hci_source, + hci_sink, + ): + print(color('>>> Connected', 'green')) + + peripheral_address = DEFAULT_PERIPHERAL_ADDRESS + self.device = Device.with_hci( + DEFAULT_PERIPHERAL_NAME, peripheral_address, hci_source, hci_sink + ) + self.device.listener = self + self.mode = self.mode_factory(self.device) + self.role = self.role_factory(self.mode) + self.device.classic_enabled = self.classic + + await self.device.power_on() + + if self.classic: + await self.device.set_discoverable(True) + await self.device.set_connectable(True) + else: + await self.device.start_advertising(auto_restart=True) + + if self.classic: + print( + color( + '### Waiting for connection on' + f' {self.device.public_address}...', + 'cyan', + ) + ) + else: + print( + color( + f'### Waiting for connection on {peripheral_address}...', + 'cyan', + ) + ) + await self.connected.wait() + print(color('### Connected', 'cyan')) + + await self.mode.on_connection(self.connection) + await self.role.run() + await asyncio.sleep(DEFAULT_LINGER_TIME) + + def on_connection(self, connection): + connection.listener = self + self.connection = connection + self.connected.set() + + def on_disconnection(self, reason): + print(color(f'!!! Disconnection: reason={reason}', 'red')) + self.connection = None + self.role.reset() + + def on_connection_parameters_update(self): + print_connection(self.connection) + + def on_connection_phy_update(self): + print_connection(self.connection) + + def on_connection_att_mtu_update(self): + print_connection(self.connection) + + def on_connection_data_length_change(self): + print_connection(self.connection) + + +# ----------------------------------------------------------------------------- +def create_mode_factory(ctx, default_mode): + mode = ctx.obj['mode'] + if mode is None: + mode = default_mode + + def create_mode(device): + if mode == 'gatt-client': + return GattClient(device, att_mtu=ctx.obj['att_mtu']) + + if mode == 'gatt-server': + return GattServer(device) + + if mode == 'l2cap-client': + return L2capClient(device) + + if mode == 'l2cap-server': + return L2capServer(device) + + if mode == 'rfcomm-client': + return RfcommClient(device) + + if mode == 'rfcomm-server': + return RfcommServer(device) + + raise ValueError('invalid mode') + + return create_mode + + +# ----------------------------------------------------------------------------- +def create_role_factory(ctx, default_role): + role = ctx.obj['role'] + if role is None: + role = default_role + + def create_role(packet_io): + if role == 'sender': + return Sender( + packet_io, + start_delay=ctx.obj['start_delay'], + packet_size=ctx.obj['packet_size'], + packet_count=ctx.obj['packet_count'], + ) + + if role == 'receiver': + return Receiver(packet_io) + + if role == 'ping': + return Ping( + packet_io, + start_delay=ctx.obj['start_delay'], + packet_size=ctx.obj['packet_size'], + packet_count=ctx.obj['packet_count'], + ) + + if role == 'pong': + return Pong(packet_io) + + raise ValueError('invalid role') + + return create_role + + +# ----------------------------------------------------------------------------- +# Main +# ----------------------------------------------------------------------------- +@click.group() +@click.option('--device-config', metavar='FILENAME', help='Device configuration file') +@click.option('--role', type=click.Choice(['sender', 'receiver', 'ping', 'pong'])) +@click.option( + '--mode', + type=click.Choice( + [ + 'gatt-client', + 'gatt-server', + 'l2cap-client', + 'l2cap-server', + 'rfcomm-client', + 'rfcomm-server', + ] + ), +) +@click.option( + '--att-mtu', + metavar='MTU', + type=click.IntRange(23, 517), + help='GATT MTU (gatt-client mode)', +) +@click.option( + '--packet-size', + '-s', + metavar='SIZE', + type=click.IntRange(8, 4096), + default=500, + help='Packet size (server role)', +) +@click.option( + '--packet-count', + '-c', + metavar='COUNT', + type=int, + default=10, + help='Packet count (server role)', +) +@click.option( + '--start-delay', + '-sd', + metavar='SECONDS', + type=int, + default=1, + help='Start delay (server role)', +) +@click.pass_context +def bench( + ctx, device_config, role, mode, att_mtu, packet_size, packet_count, start_delay +): + ctx.ensure_object(dict) + ctx.obj['device_config'] = device_config + ctx.obj['role'] = role + ctx.obj['mode'] = mode + ctx.obj['att_mtu'] = att_mtu + ctx.obj['packet_size'] = packet_size + ctx.obj['packet_count'] = packet_count + ctx.obj['start_delay'] = start_delay + + ctx.obj['classic'] = mode in ('rfcomm-client', 'rfcomm-server') + + +@bench.command() +@click.argument('transport') +@click.option( + '--peripheral', + 'peripheral_address', + metavar='ADDRESS_OR_NAME', + default=DEFAULT_PERIPHERAL_ADDRESS, + help='Address or name to connect to', +) +@click.option( + '--connection-interval', + '--ci', + metavar='CONNECTION_INTERVAL', + type=int, + help='Connection interval (in ms)', +) +@click.option('--phy', type=click.Choice(['1m', '2m', 'coded']), help='PHY to use') +@click.pass_context +def central(ctx, transport, peripheral_address, connection_interval, phy): + """Run as a central (initiates the connection)""" + role_factory = create_role_factory(ctx, 'sender') + mode_factory = create_mode_factory(ctx, 'gatt-client') + classic = ctx.obj['classic'] + + asyncio.run( + Central( + transport, + peripheral_address, + classic, + role_factory, + mode_factory, + connection_interval, + phy, + ).run() + ) + + +@bench.command() +@click.argument('transport') +@click.pass_context +def peripheral(ctx, transport): + """Run as a peripheral (waits for a connection)""" + role_factory = create_role_factory(ctx, 'receiver') + mode_factory = create_mode_factory(ctx, 'gatt-server') + + asyncio.run( + Peripheral(transport, ctx.obj['classic'], role_factory, mode_factory).run() + ) + + +def main(): + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + bench() + + +# ----------------------------------------------------------------------------- +if __name__ == "__main__": + main() # pylint: disable=no-value-for-parameter diff --git a/apps/console.py b/apps/console.py index 3ac4957..0ea9e5b 100644 --- a/apps/console.py +++ b/apps/console.py @@ -20,18 +20,16 @@ # Imports # ----------------------------------------------------------------------------- import asyncio -from bumble.hci import HCI_Constant -import os -import os.path import logging -import click +import os +import random +import re +import humanize +from typing import Optional, Union from collections import OrderedDict -import colors -from bumble.core import UUID, AdvertisingData -from bumble.device import Device, Connection, Peer -from bumble.utils import AsyncRunner -from bumble.transport import open_transport_or_link +import click +from prettytable import PrettyTable from prompt_toolkit import Application from prompt_toolkit.history import FileHistory @@ -42,6 +40,7 @@ from prompt_toolkit.styles import Style from prompt_toolkit.filters import Condition from prompt_toolkit.widgets import TextArea, Frame from prompt_toolkit.widgets.toolbars import FormattedTextToolbar +from prompt_toolkit.data_structures import Point from prompt_toolkit.layout import ( Layout, HSplit, @@ -50,41 +49,100 @@ from prompt_toolkit.layout import ( Float, FormattedTextControl, FloatContainer, - ConditionalContainer + ConditionalContainer, + Dimension, +) + +from bumble import __version__ +import bumble.core +from bumble import colors +from bumble.core import UUID, AdvertisingData, BT_LE_TRANSPORT +from bumble.device import ConnectionParametersPreferences, Device, Connection, Peer +from bumble.utils import AsyncRunner +from bumble.transport import open_transport_or_link +from bumble.gatt import Characteristic, Service, CharacteristicDeclaration, Descriptor +from bumble.gatt_client import CharacteristicProxy +from bumble.hci import ( + HCI_Constant, + HCI_LE_1M_PHY, + HCI_LE_2M_PHY, + HCI_LE_CODED_PHY, ) + # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- -BUMBLE_USER_DIR = os.path.expanduser('~/.bumble') -DEFAULT_PROMPT_HEIGHT = 20 +BUMBLE_USER_DIR = os.path.expanduser('~/.bumble') DEFAULT_RSSI_BAR_WIDTH = 20 -DISPLAY_MIN_RSSI = -100 -DISPLAY_MAX_RSSI = -30 +DEFAULT_CONNECTION_TIMEOUT = 30.0 +DISPLAY_MIN_RSSI = -100 +DISPLAY_MAX_RSSI = -30 +RSSI_MONITOR_INTERVAL = 5.0 # Seconds + # ----------------------------------------------------------------------------- -# Globals +# Utils # ----------------------------------------------------------------------------- -App = None + + +def le_phy_name(phy_id): + return {HCI_LE_1M_PHY: '1M', HCI_LE_2M_PHY: '2M', HCI_LE_CODED_PHY: 'CODED'}.get( + phy_id, HCI_Constant.le_phy_name(phy_id) + ) + + +def rssi_bar(rssi): + blocks = ['', 'â–', 'â–Ž', 'â–', 'â–Œ', 'â–‹', 'â–Š', 'â–‰'] + bar_width = (rssi - DISPLAY_MIN_RSSI) / (DISPLAY_MAX_RSSI - DISPLAY_MIN_RSSI) + bar_width = min(max(bar_width, 0), 1) + bar_ticks = int(bar_width * DEFAULT_RSSI_BAR_WIDTH * 8) + bar_blocks = ('â–ˆ' * int(bar_ticks / 8)) + blocks[bar_ticks % 8] + return f'{rssi:4} {bar_blocks}' + + +def parse_phys(phys): + if phys.lower() == '*': + return None + + phy_list = [] + elements = phys.lower().split(',') + for element in elements: + if element == '1m': + phy_list.append(HCI_LE_1M_PHY) + elif element == '2m': + phy_list.append(HCI_LE_2M_PHY) + elif element == 'coded': + phy_list.append(HCI_LE_CODED_PHY) + else: + raise ValueError('invalid PHY name') + return phy_list # ----------------------------------------------------------------------------- # Console App # ----------------------------------------------------------------------------- class ConsoleApp: + connected_peer: Optional[Peer] + def __init__(self): self.known_addresses = set() - self.known_attributes = [] + self.known_remote_attributes = [] + self.known_local_attributes = [] self.device = None self.connected_peer = None - self.top_tab = 'scan' - - style = Style.from_dict({ - 'output-field': 'bg:#000044 #ffffff', - 'input-field': 'bg:#000000 #ffffff', - 'line': '#004400', - 'error': 'fg:ansired' - }) + self.top_tab = 'device' + self.monitor_rssi = False + self.connection_rssi = None + + style = Style.from_dict( + { + 'output-field': 'bg:#000044 #ffffff', + 'input-field': 'bg:#000000 #ffffff', + 'line': '#004400', + 'error': 'fg:ansired', + } + ) class LiveCompleter(Completer): def __init__(self, words): @@ -96,34 +154,40 @@ class ConsoleApp: yield Completion(word, start_position=-len(prefix)) def make_completer(): - return NestedCompleter.from_nested_dict({ - 'scan': { - 'on': None, - 'off': None - }, - 'advertise': { - 'on': None, - 'off': None - }, - 'show': { - 'scan': None, - 'services': None, - 'attributes': None, - 'log': None - }, - 'connect': LiveCompleter(self.known_addresses), - 'update-parameters': None, - 'encrypt': None, - 'disconnect': None, - 'discover': { - 'services': None, - 'attributes': None - }, - 'read': LiveCompleter(self.known_attributes), - 'write': LiveCompleter(self.known_attributes), - 'quit': None, - 'exit': None - }) + return NestedCompleter.from_nested_dict( + { + 'scan': {'on': None, 'off': None, 'clear': None}, + 'advertise': {'on': None, 'off': None}, + 'rssi': {'on': None, 'off': None}, + 'show': { + 'scan': None, + 'log': None, + 'device': None, + 'local-services': None, + 'remote-services': None, + 'local-values': None, + 'remote-values': None, + }, + 'filter': { + 'address': None, + }, + 'connect': LiveCompleter(self.known_addresses), + 'update-parameters': None, + 'encrypt': None, + 'disconnect': None, + 'discover': {'services': None, 'attributes': None}, + 'request-mtu': None, + 'read': LiveCompleter(self.known_remote_attributes), + 'write': LiveCompleter(self.known_remote_attributes), + 'local-write': LiveCompleter(self.known_local_attributes), + 'subscribe': LiveCompleter(self.known_remote_attributes), + 'unsubscribe': LiveCompleter(self.known_remote_attributes), + 'set-phy': {'1m': None, '2m': None, 'coded': None}, + 'set-default-phy': None, + 'quit': None, + 'exit': None, + } + ) self.input_field = TextArea( height=1, @@ -131,43 +195,65 @@ class ConsoleApp: multiline=False, wrap_lines=False, completer=make_completer(), - history=FileHistory(os.path.join(BUMBLE_USER_DIR, 'history')) + history=FileHistory(os.path.join(BUMBLE_USER_DIR, 'history')), ) self.input_field.accept_handler = self.accept_input - self.output_height = 7 + self.output_height = Dimension(min=7, max=7, weight=1) self.output_lines = [] - self.output = FormattedTextControl() + self.output = FormattedTextControl( + get_cursor_position=lambda: Point(0, max(0, len(self.output_lines) - 1)) + ) + self.output_max_lines = 20 self.scan_results_text = FormattedTextControl() - self.services_text = FormattedTextControl() - self.attributes_text = FormattedTextControl() - self.log_text = FormattedTextControl() - self.log_height = 20 + self.local_services_text = FormattedTextControl() + self.remote_services_text = FormattedTextControl() + self.device_text = FormattedTextControl() + self.log_text = FormattedTextControl( + get_cursor_position=lambda: Point(0, max(0, len(self.log_lines) - 1)) + ) + self.local_values_text = FormattedTextControl() + self.remote_values_text = FormattedTextControl() + self.log_height = Dimension(min=7, weight=4) + self.log_max_lines = 100 self.log_lines = [] - container = HSplit([ - ConditionalContainer( - Frame(Window(self.scan_results_text), title='Scan Results'), - filter=Condition(lambda: self.top_tab == 'scan') - ), - ConditionalContainer( - Frame(Window(self.services_text), title='Services'), - filter=Condition(lambda: self.top_tab == 'services') - ), - ConditionalContainer( - Frame(Window(self.attributes_text), title='Attributes'), - filter=Condition(lambda: self.top_tab == 'attributes') - ), - ConditionalContainer( - Frame(Window(self.log_text), title='Log'), - filter=Condition(lambda: self.top_tab == 'log') - ), - Frame(Window(self.output), height=self.output_height), - # HorizontalLine(), - FormattedTextToolbar(text=self.get_status_bar_text, style='reverse'), - self.input_field - ]) + container = HSplit( + [ + ConditionalContainer( + Frame(Window(self.scan_results_text), title='Scan Results'), + filter=Condition(lambda: self.top_tab == 'scan'), + ), + ConditionalContainer( + Frame(Window(self.local_services_text), title='Local Services'), + filter=Condition(lambda: self.top_tab == 'local-services'), + ), + ConditionalContainer( + Frame(Window(self.local_values_text), title='Local Values'), + filter=Condition(lambda: self.top_tab == 'local-values'), + ), + ConditionalContainer( + Frame(Window(self.remote_services_text), title='Remote Services'), + filter=Condition(lambda: self.top_tab == 'remote-services'), + ), + ConditionalContainer( + Frame(Window(self.remote_values_text), title='Remote Values'), + filter=Condition(lambda: self.top_tab == 'remote-values'), + ), + ConditionalContainer( + Frame(Window(self.log_text, height=self.log_height), title='Log'), + filter=Condition(lambda: self.top_tab == 'log'), + ), + ConditionalContainer( + Frame(Window(self.device_text), title='Device'), + filter=Condition(lambda: self.top_tab == 'device'), + ), + Frame(Window(self.output, height=self.output_height)), + FormattedTextToolbar(text=self.get_status_bar_text, style='reverse'), + self.input_field, + ] + ) container = FloatContainer( container, @@ -182,35 +268,50 @@ class ConsoleApp: layout = Layout(container, focused_element=self.input_field) - kb = KeyBindings() - @kb.add("c-c") - @kb.add("c-q") + key_bindings = KeyBindings() + + @key_bindings.add("c-c") + @key_bindings.add("c-q") def _(event): event.app.exit() + # pylint: disable=invalid-name self.ui = Application( - layout=layout, - style=style, - key_bindings=kb, - full_screen=True + layout=layout, style=style, key_bindings=key_bindings, full_screen=True ) async def run_async(self, device_config, transport): + rssi_monitoring_task = asyncio.create_task(self.rssi_monitor_loop()) + async with await open_transport_or_link(transport) as (hci_source, hci_sink): if device_config: - self.device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink) + self.device = Device.from_config_file_with_hci( + device_config, hci_source, hci_sink + ) else: - self.device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink) + random_address = ( + f"{random.randint(192,255):02X}" # address is static random + ) + for random_byte in random.sample(range(255), 5): + random_address += f":{random_byte:02X}" + self.append_to_log(f"Setting random address: {random_address}") + self.device = Device.with_hci( + 'Bumble', random_address, hci_source, hci_sink + ) self.device.listener = DeviceListener(self) await self.device.power_on() + self.show_device(self.device) + self.show_local_services(self.device.gatt_server.attributes) # Run the UI await self.ui.run_async() + rssi_monitoring_task.cancel() + def add_known_address(self, address): self.known_addresses.add(address) - def accept_input(self, buff): + def accept_input(self, _): if len(self.input_field.text) == 0: return self.append_to_output([('', '* '), ('ansicyan', self.input_field.text)], False) @@ -221,25 +322,50 @@ class ConsoleApp: connection_state = 'NONE' encryption_state = '' + att_mtu = '' + rssi = '' if self.connection_rssi is None else rssi_bar(self.connection_rssi) if self.device: - if self.device.is_connecting: + if self.device.is_le_connecting: connection_state = 'CONNECTING' elif self.connected_peer: connection = self.connected_peer.connection - connection_parameters = f'{connection.parameters.connection_interval}/{connection.parameters.connection_latency}/{connection.parameters.supervision_timeout}' - connection_state = f'{connection.peer_address} {connection_parameters} {connection.data_length}' - encryption_state = 'ENCRYPTED' if connection.is_encrypted else 'NOT ENCRYPTED' + connection_parameters = ( + f'{connection.parameters.connection_interval}/' + f'{connection.parameters.peripheral_latency}/' + f'{connection.parameters.supervision_timeout}' + ) + if connection.transport == BT_LE_TRANSPORT: + phy_state = ( + f' RX={le_phy_name(connection.phy.rx_phy)}/' + f'TX={le_phy_name(connection.phy.tx_phy)}' + ) + else: + phy_state = '' + connection_state = ( + f'{connection.peer_address} ' + f'{connection_parameters} ' + f'{connection.data_length}' + f'{phy_state}' + ) + encryption_state = ( + 'ENCRYPTED' if connection.is_encrypted else 'NOT ENCRYPTED' + ) + att_mtu = f'ATT_MTU: {connection.att_mtu}' return [ ('ansigreen', f' SCAN: {scanning} '), ('', ' '), ('ansiblue', f' CONNECTION: {connection_state} '), ('', ' '), - ('ansimagenta', f' {encryption_state} ') + ('ansimagenta', f' {encryption_state} '), + ('', ' '), + ('ansicyan', f' {att_mtu} '), + ('', ' '), + ('ansiyellow', f' {rssi} '), ] - def show_error(self, title, details = None): + def show_error(self, title, details=None): appended = [('class:error', title)] if details: appended.append(('', f' {details}')) @@ -254,36 +380,108 @@ class ConsoleApp: self.scan_results_text.text = ANSI('\n'.join(lines)) self.ui.invalidate() - def show_services(self, services): + def show_remote_services(self, services): lines = [] - del self.known_attributes[:] + del self.known_remote_attributes[:] for service in services: - lines.append(('ansicyan', str(service) + '\n')) + lines.append(("ansicyan", f"{service}\n")) for characteristic in service.characteristics: - lines.append(('ansimagenta', ' ' + str(characteristic) + '\n')) - self.known_attributes.append(f'{service.uuid.to_hex_str()}.{characteristic.uuid.to_hex_str()}') - self.known_attributes.append(f'*.{characteristic.uuid.to_hex_str()}') - self.known_attributes.append(f'#{characteristic.handle:X}') + lines.append(('ansimagenta', f' {characteristic} + \n')) + self.known_remote_attributes.append( + f'{service.uuid.to_hex_str()}.{characteristic.uuid.to_hex_str()}' + ) + self.known_remote_attributes.append( + f'*.{characteristic.uuid.to_hex_str()}' + ) + self.known_remote_attributes.append(f'#{characteristic.handle:X}') for descriptor in characteristic.descriptors: - lines.append(('ansigreen', ' ' + str(descriptor) + '\n')) + lines.append(("ansigreen", f" {descriptor}\n")) - self.services_text.text = lines + self.remote_services_text.text = lines self.ui.invalidate() - async def show_attributes(self, attributes): + def show_local_services(self, attributes): lines = [] - + del self.known_local_attributes[:] for attribute in attributes: - lines.append(('ansicyan', f'{attribute}\n')) + if isinstance(attribute, Service): + # Save the most recent service for use later + service = attribute + lines.append(("ansicyan", f"{attribute}\n")) + elif isinstance(attribute, Characteristic): + # CharacteristicDeclaration includes all info from Characteristic + # no need to print it twice + continue + elif isinstance(attribute, CharacteristicDeclaration): + # Save the most recent characteristic declaration for use later + characteristic_declaration = attribute + self.known_local_attributes.append( + f'{service.uuid.to_hex_str()}.{attribute.characteristic.uuid.to_hex_str()}' + ) + self.known_local_attributes.append( + f'#{attribute.characteristic.handle:X}' + ) + lines.append(("ansimagenta", f" {attribute}\n")) + elif isinstance(attribute, Descriptor): + self.known_local_attributes.append( + f'{service.uuid.to_hex_str()}.{characteristic_declaration.characteristic.uuid.to_hex_str()}.{attribute.type.to_hex_str()}' + ) + self.known_local_attributes.append(f'#{attribute.handle:X}') + lines.append(("ansigreen", f" {attribute}\n")) + else: + lines.append(("ansiyellow", f"{attribute}\n")) - self.attributes_text.text = lines + self.local_services_text.text = lines + self.ui.invalidate() + + def show_device(self, device): + lines = [] + + lines.append(('ansicyan', 'Bumble Version: ')) + lines.append(('', f'{__version__}\n')) + lines.append(('ansicyan', 'Name: ')) + lines.append(('', f'{device.name}\n')) + lines.append(('ansicyan', 'Public Address: ')) + lines.append(('', f'{device.public_address}\n')) + lines.append(('ansicyan', 'Random Address: ')) + lines.append(('', f'{device.random_address}\n')) + lines.append(('ansicyan', 'LE Enabled: ')) + lines.append(('', f'{device.le_enabled}\n')) + lines.append(('ansicyan', 'Classic Enabled: ')) + lines.append(('', f'{device.classic_enabled}\n')) + lines.append(('ansicyan', 'Classic SC Enabled: ')) + lines.append(('', f'{device.classic_sc_enabled}\n')) + lines.append(('ansicyan', 'Classic SSP Enabled: ')) + lines.append(('', f'{device.classic_ssp_enabled}\n')) + lines.append(('ansicyan', 'Classic Class: ')) + lines.append(('', f'{device.class_of_device}\n')) + lines.append(('ansicyan', 'Discoverable: ')) + lines.append(('', f'{device.discoverable}\n')) + lines.append(('ansicyan', 'Connectable: ')) + lines.append(('', f'{device.connectable}\n')) + lines.append(('ansicyan', 'Advertising Data: ')) + lines.append(('', f'{device.advertising_data}\n')) + lines.append(('ansicyan', 'Scan Response Data: ')) + lines.append(('', f'{device.scan_response_data}\n')) + advertising_interval = ( + device.advertising_interval_min + if device.advertising_interval_min == device.advertising_interval_max + else ( + f'{device.advertising_interval_min} to ' + f'{device.advertising_interval_max}' + ) + ) + lines.append(('ansicyan', 'Advertising Interval: ')) + lines.append(('', f'{advertising_interval}\n')) + + self.device_text.text = lines self.ui.invalidate() def append_to_output(self, line, invalidate=True): - if type(line) is str: + if isinstance(line, str): line = [('', line)] - self.output_lines = self.output_lines[-(self.output_height - 3):] + self.output_lines = self.output_lines[-self.output_max_lines :] self.output_lines.append(line) formatted_text = [] for line in self.output_lines: @@ -295,7 +493,7 @@ class ConsoleApp: def append_to_log(self, lines, invalidate=True): self.log_lines.extend(lines.split('\n')) - self.log_lines = self.log_lines[-(self.log_height - 3):] + self.log_lines = self.log_lines[-self.log_max_lines :] self.log_text.text = ANSI('\n'.join(self.log_lines)) if invalidate: self.ui.invalidate() @@ -308,7 +506,10 @@ class ConsoleApp: # Discover all services, characteristics and descriptors self.append_to_output('discovering services...') await self.connected_peer.discover_services() - self.append_to_output(f'found {len(self.connected_peer.services)} services, discovering charateristics...') + self.append_to_output( + f'found {len(self.connected_peer.services)} services,' + ' discovering characteristics...' + ) await self.connected_peer.discover_characteristics() self.append_to_output('found characteristics, discovering descriptors...') for service in self.connected_peer.services: @@ -316,7 +517,7 @@ class ConsoleApp: await self.connected_peer.discover_descriptors(characteristic) self.append_to_output('discovery completed') - self.show_services(self.connected_peer.services) + self.show_remote_services(self.connected_peer.services) async def discover_attributes(self): if not self.connected_peer: @@ -328,11 +529,71 @@ class ConsoleApp: attributes = await self.connected_peer.discover_attributes() self.append_to_output(f'discovered {len(attributes)} attributes...') - await self.show_attributes(attributes) + self.show_attributes(attributes) + + def find_remote_characteristic(self, param) -> Optional[CharacteristicProxy]: + if not self.connected_peer: + return None + parts = param.split('.') + if len(parts) == 2: + service_uuid = UUID(parts[0]) if parts[0] != '*' else None + characteristic_uuid = UUID(parts[1]) + for service in self.connected_peer.services: + if service_uuid is None or service.uuid == service_uuid: + for characteristic in service.characteristics: + if characteristic.uuid == characteristic_uuid: + return characteristic + elif len(parts) == 1: + if parts[0].startswith('#'): + attribute_handle = int(f'{parts[0][1:]}', 16) + for service in self.connected_peer.services: + for characteristic in service.characteristics: + if characteristic.handle == attribute_handle: + return characteristic + + return None + + def find_local_attribute( + self, param + ) -> Optional[Union[Characteristic, Descriptor]]: + parts = param.split('.') + if len(parts) == 3: + service_uuid = UUID(parts[0]) + characteristic_uuid = UUID(parts[1]) + descriptor_uuid = UUID(parts[2]) + return self.device.gatt_server.get_descriptor_attribute( + service_uuid, characteristic_uuid, descriptor_uuid + ) + if len(parts) == 2: + service_uuid = UUID(parts[0]) + characteristic_uuid = UUID(parts[1]) + characteristic_attributes = ( + self.device.gatt_server.get_characteristic_attributes( + service_uuid, characteristic_uuid + ) + ) + if characteristic_attributes: + return characteristic_attributes[1] + return None + elif len(parts) == 1: + if parts[0].startswith('#'): + attribute_handle = int(f'{parts[0][1:]}', 16) + attribute = self.device.gatt_server.get_attribute(attribute_handle) + if isinstance(attribute, (Characteristic, Descriptor)): + return attribute + return None + + return None + + async def rssi_monitor_loop(self): + while True: + if self.monitor_rssi and self.connected_peer: + self.connection_rssi = await self.connected_peer.connection.get_rssi() + await asyncio.sleep(RSSI_MONITOR_INTERVAL) async def command(self, command): try: - (keyword, *params) = command.strip().split(' ', 1) + (keyword, *params) = command.strip().split(' ') keyword = keyword.replace('-', '_').lower() handler = getattr(self, f'do_{keyword}', None) if handler: @@ -351,50 +612,109 @@ class ConsoleApp: else: await self.device.start_scanning() elif params[0] == 'on': + if len(params) == 2: + if not params[1].startswith("filter="): + self.show_error( + 'invalid syntax', + 'expected address filter=key1:value1,key2:value,... ' + 'available filters: address', + ) + # regex: (word):(any char except ,) + matches = re.findall(r"(\w+):([^,]+)", params[1]) + for match in matches: + if match[0] == "address": + self.device.listener.address_filter = match[1] + await self.device.start_scanning() self.top_tab = 'scan' elif params[0] == 'off': await self.device.stop_scanning() + elif params[0] == 'clear': + self.device.listener.scan_results.clear() + self.known_addresses.clear() + self.show_scan_results(self.device.listener.scan_results) else: self.show_error('unsupported arguments for scan command') + async def do_rssi(self, params): + if len(params) == 0: + # Toggle monitoring + self.monitor_rssi = not self.monitor_rssi + elif params[0] == 'on': + self.monitor_rssi = True + elif params[0] == 'off': + self.monitor_rssi = False + else: + self.show_error('unsupported arguments for rssi command') + async def do_connect(self, params): - if len(params) != 1: - self.show_error('invalid syntax', 'expected connect <address>') + if len(params) != 1 and len(params) != 2: + self.show_error('invalid syntax', 'expected connect <address> [phys]') return + if len(params) == 1: + phys = None + else: + phys = parse_phys(params[1]) + if phys is None: + connection_parameters_preferences = None + else: + connection_parameters_preferences = { + phy: ConnectionParametersPreferences() for phy in phys + } + + if self.device.is_scanning: + await self.device.stop_scanning() + self.append_to_output('connecting...') - await self.device.connect(params[0]) - self.top_tab = 'services' - async def do_disconnect(self, params): - if not self.connected_peer: - self.show_error('not connected') - return + try: + await self.device.connect( + params[0], + connection_parameters_preferences=connection_parameters_preferences, + timeout=DEFAULT_CONNECTION_TIMEOUT, + ) + self.top_tab = 'services' + except bumble.core.TimeoutError: + self.show_error('connection timed out') + + async def do_disconnect(self, _): + if self.device.is_le_connecting: + await self.device.cancel_connection() + else: + if not self.connected_peer: + self.show_error('not connected') + return - await self.connected_peer.connection.disconnect() + await self.connected_peer.connection.disconnect() async def do_update_parameters(self, params): if len(params) != 1 or len(params[0].split('/')) != 3: - self.show_error('invalid syntax', 'expected update-parameters <interval-min>-<interval-max>/<latency>/<supervision>') + self.show_error( + 'invalid syntax', + 'expected update-parameters <interval-min>-<interval-max>' + '/<max-latency>/<supervision>', + ) return if not self.connected_peer: self.show_error('not connected') return - connection_intervals, connection_latency, supervision_timeout = params[0].split('/') - connection_interval_min, connection_interval_max = [int(x) for x in connection_intervals.split('-')] - connection_latency = int(connection_latency) + connection_intervals, max_latency, supervision_timeout = params[0].split('/') + connection_interval_min, connection_interval_max = [ + int(x) for x in connection_intervals.split('-') + ] + max_latency = int(max_latency) supervision_timeout = int(supervision_timeout) await self.connected_peer.connection.update_parameters( connection_interval_min, connection_interval_max, - connection_latency, - supervision_timeout + max_latency, + supervision_timeout, ) - async def do_encrypt(self, params): + async def do_encrypt(self, _): if not self.connected_peer: self.show_error('not connected') return @@ -417,10 +737,137 @@ class ConsoleApp: async def do_show(self, params): if params: - if params[0] in {'scan', 'services', 'attributes', 'log'}: + if params[0] in { + 'scan', + 'log', + 'device', + 'local-services', + 'remote-services', + 'local-values', + 'remote-values', + }: self.top_tab = params[0] self.ui.invalidate() + while self.top_tab == 'local-values': + await self.do_show_local_values() + await asyncio.sleep(1) + + while self.top_tab == 'remote-values': + await self.do_show_remote_values() + await asyncio.sleep(1) + + async def do_show_local_values(self): + prettytable = PrettyTable() + field_names = ["Service", "Characteristic", "Descriptor"] + + # if there's no connections, add a column just for value + if not self.device.connections: + field_names.append("Value") + + # if there are connections, add a column for each connection's value + for connection in self.device.connections.values(): + field_names.append(f"Connection {connection.handle}") + + for attribute in self.device.gatt_server.attributes: + if isinstance(attribute, Characteristic): + service = self.device.gatt_server.get_attribute_group( + attribute.handle, Service + ) + if not service: + continue + values = [ + attribute.read_value(connection) + for connection in self.device.connections.values() + ] + if not values: + values = [attribute.read_value(None)] + prettytable.add_row([f"{service.uuid}", attribute.uuid, ""] + values) + + elif isinstance(attribute, Descriptor): + service = self.device.gatt_server.get_attribute_group( + attribute.handle, Service + ) + if not service: + continue + characteristic = self.device.gatt_server.get_attribute_group( + attribute.handle, Characteristic + ) + if not characteristic: + continue + values = [ + attribute.read_value(connection) + for connection in self.device.connections.values() + ] + if not values: + values = [attribute.read_value(None)] + + # TODO: future optimization: convert CCCD value to human readable string + + prettytable.add_row( + [service.uuid, characteristic.uuid, attribute.type] + values + ) + + prettytable.field_names = field_names + self.local_values_text.text = prettytable.get_string() + self.ui.invalidate() + + async def do_show_remote_values(self): + prettytable = PrettyTable( + field_names=[ + "Connection", + "Service", + "Characteristic", + "Descriptor", + "Time", + "Value", + ] + ) + for connection in self.device.connections.values(): + for handle, (time, value) in connection.gatt_client.cached_values.items(): + row = [connection.handle] + attribute = connection.gatt_client.get_attributes(handle) + if not attribute: + continue + if len(attribute) == 3: + row.extend( + [attribute[0].uuid, attribute[1].uuid, attribute[2].type] + ) + elif len(attribute) == 2: + row.extend([attribute[0].uuid, attribute[1].uuid, ""]) + elif len(attribute) == 1: + row.extend([attribute[0].uuid, "", ""]) + else: + continue + + row.extend([humanize.naturaltime(time), value]) + prettytable.add_row(row) + + self.remote_values_text.text = prettytable.get_string() + self.ui.invalidate() + + async def do_get_phy(self, _): + if not self.connected_peer: + self.show_error('not connected') + return + + phy = await self.connected_peer.connection.get_phy() + self.append_to_output( + f'PHY: RX={HCI_Constant.le_phy_name(phy[0])}, ' + f'TX={HCI_Constant.le_phy_name(phy[1])}' + ) + + async def do_request_mtu(self, params): + if len(params) != 1: + self.show_error('invalid syntax', 'expected request-mtu <mtu>') + return + + if not self.connected_peer: + self.show_error('not connected') + return + + await self.connected_peer.request_mtu(int(params[0])) + async def do_discover(self, params): if not params: self.show_error('invalid syntax', 'expected discover services|attributes') @@ -433,41 +880,164 @@ class ConsoleApp: await self.discover_attributes() async def do_read(self, params): + if len(params) != 1: + self.show_error('invalid syntax', 'expected read <attribute>') + return + if not self.connected_peer: self.show_error('not connected') return - if len(params) != 1: - self.show_error('invalid syntax', 'expected read <attribute>') + characteristic = self.find_remote_characteristic(params[0]) + if characteristic is None: + self.show_error('no such characteristic') return - parts = params[0].split('.') - if len(parts) == 2: - service_uuid = UUID(parts[0]) if parts[0] != '*' else None - characteristic_uuid = UUID(parts[1]) - for service in self.connected_peer.services: - if service_uuid is None or service.uuid == service_uuid: - for characteristic in service.characteristics: - if characteristic.uuid == characteristic_uuid: - value = await self.connected_peer.read_value(characteristic) - self.append_to_output(f'VALUE: {value}') - return + value = await characteristic.read_value() + self.append_to_output(f'VALUE: 0x{value.hex()}') + + async def do_write(self, params): + if not self.connected_peer: + self.show_error('not connected') + return + + if len(params) != 2: + self.show_error('invalid syntax', 'expected write <attribute> <value>') + return + + if params[1].upper().startswith("0X"): + value = bytes.fromhex(params[1][2:]) # parse as hex string + else: + try: + value = int(params[1]) # try as integer + except ValueError: + value = str.encode(params[1]) # must be a string + + characteristic = self.find_remote_characteristic(params[0]) + if characteristic is None: self.show_error('no such characteristic') - elif len(parts) == 1: - if parts[0].startswith('#'): - attribute_handle = int(f'{parts[0][1:]}', 16) - value = await self.connected_peer.read_value(attribute_handle) - self.append_to_output(f'VALUE: {value}') - return + return + + # use write with response if supported + with_response = characteristic.properties & Characteristic.Properties.WRITE + await characteristic.write_value(value, with_response=with_response) + + async def do_local_write(self, params): + if len(params) != 2: + self.show_error( + 'invalid syntax', 'expected local-write <attribute> <value>' + ) + return + + if params[1].upper().startswith("0X"): + value = bytes.fromhex(params[1][2:]) # parse as hex string else: + try: + value = int(params[1]).to_bytes(2, "little") # try as 2 byte integer + except ValueError: + value = str.encode(params[1]) # must be a string + + attribute = self.find_local_attribute(params[0]) + if not attribute: + self.show_error('invalid syntax', 'unable to find attribute') + return + + # send data to any subscribers + if isinstance(attribute, Characteristic): + attribute.write_value(None, value) + if attribute.has_properties(Characteristic.NOTIFY): + await self.device.gatt_server.notify_subscribers(attribute) + if attribute.has_properties(Characteristic.INDICATE): + await self.device.gatt_server.indicate_subscribers(attribute) + + async def do_subscribe(self, params): + if not self.connected_peer: + self.show_error('not connected') + return + + if len(params) != 1: + self.show_error('invalid syntax', 'expected subscribe <attribute>') + return + + characteristic = self.find_remote_characteristic(params[0]) + if characteristic is None: self.show_error('no such characteristic') + return - async def do_exit(self, params): + await characteristic.subscribe( + lambda value: self.append_to_output( + f"{characteristic} VALUE: 0x{value.hex()}" + ), + ) + + async def do_unsubscribe(self, params): + if not self.connected_peer: + self.show_error('not connected') + return + + if len(params) != 1: + self.show_error('invalid syntax', 'expected subscribe <attribute>') + return + + characteristic = self.find_remote_characteristic(params[0]) + if characteristic is None: + self.show_error('no such characteristic') + return + + await characteristic.unsubscribe() + + async def do_set_phy(self, params): + if len(params) != 1: + self.show_error( + 'invalid syntax', 'expected set-phy <tx_rx_phys>|<tx_phys>/<rx_phys>' + ) + return + + if not self.connected_peer: + self.show_error('not connected') + return + + if '/' in params[0]: + tx_phys, rx_phys = params[0].split('/') + else: + tx_phys = params[0] + rx_phys = tx_phys + + await self.connected_peer.connection.set_phy( + tx_phys=parse_phys(tx_phys), rx_phys=parse_phys(rx_phys) + ) + + async def do_set_default_phy(self, params): + if len(params) != 1: + self.show_error( + 'invalid syntax', + 'expected set-default-phy <tx_rx_phys>|<tx_phys>/<rx_phys>', + ) + return + + if '/' in params[0]: + tx_phys, rx_phys = params[0].split('/') + else: + tx_phys = params[0] + rx_phys = tx_phys + + await self.device.set_default_phy( + tx_phys=parse_phys(tx_phys), rx_phys=parse_phys(rx_phys) + ) + + async def do_exit(self, _): self.ui.exit() - async def do_quit(self, params): + async def do_quit(self, _): self.ui.exit() + async def do_filter(self, params): + if params[0] == "address": + if len(params) != 2: + self.show_error('invalid syntax', 'expected filter address <pattern>') + return + self.device.listener.address_filter = params[1] + # ----------------------------------------------------------------------------- # Device and Connection Listener @@ -476,42 +1046,96 @@ class DeviceListener(Device.Listener, Connection.Listener): def __init__(self, app): self.app = app self.scan_results = OrderedDict() + self.address_filter = None + + @property + def address_filter(self): + return self._address_filter + + @address_filter.setter + def address_filter(self, filter_addr): + if filter_addr is None: + self._address_filter = re.compile(r".*") + else: + self._address_filter = re.compile(filter_addr) + self.scan_results = OrderedDict( + filter(self.filter_address_match, self.scan_results) + ) + self.app.show_scan_results(self.scan_results) + + def filter_address_match(self, address): + """ + Returns true if an address matches the filter + """ + return bool(self.address_filter.match(address)) @AsyncRunner.run_in_task() + # pylint: disable=invalid-overridden-method async def on_connection(self, connection): self.app.connected_peer = Peer(connection) + self.app.connection_rssi = None self.app.append_to_output(f'connected to {self.app.connected_peer}') connection.listener = self def on_disconnection(self, reason): - self.app.append_to_output(f'disconnected from {self.app.connected_peer}, reason: {HCI_Constant.error_name(reason)}') + self.app.append_to_output( + f'disconnected from {self.app.connected_peer}, ' + f'reason: {HCI_Constant.error_name(reason)}' + ) self.app.connected_peer = None + self.app.connection_rssi = None def on_connection_parameters_update(self): - self.app.append_to_output(f'connection parameters update: {self.app.connected_peer.connection.parameters}') + self.app.append_to_output( + f'connection parameters update: ' + f'{self.app.connected_peer.connection.parameters}' + ) def on_connection_phy_update(self): - self.app.append_to_output(f'connection phy update: {self.app.connected_peer.connection.phy}') + self.app.append_to_output( + f'connection phy update: {self.app.connected_peer.connection.phy}' + ) def on_connection_att_mtu_update(self): - self.app.append_to_output(f'connection att mtu update: {self.app.connected_peer.connection.att_mtu}') + self.app.append_to_output( + f'connection att mtu update: {self.app.connected_peer.connection.att_mtu}' + ) def on_connection_encryption_change(self): - self.app.append_to_output(f'connection encryption change: {"encrypted" if self.app.connected_peer.connection.is_encrypted else "not encrypted"}') + encryption_state = ( + 'encrypted' + if self.app.connected_peer.connection.is_encrypted + else 'not encrypted' + ) + self.app.append_to_output( + 'connection encryption change: ' f'{encryption_state}' + ) def on_connection_data_length_change(self): - self.app.append_to_output(f'connection data length change: {self.app.connected_peer.connection.data_length}') + self.app.append_to_output( + 'connection data length change: ' + f'{self.app.connected_peer.connection.data_length}' + ) + + def on_advertisement(self, advertisement): + if not self.filter_address_match(str(advertisement.address)): + return - def on_advertisement(self, address, ad_data, rssi, connectable): - entry_key = f'{address}/{address.address_type}' + entry_key = f'{advertisement.address}/{advertisement.address.address_type}' entry = self.scan_results.get(entry_key) if entry: - entry.ad_data = ad_data - entry.rssi = rssi - entry.connectable = connectable + entry.ad_data = advertisement.data + entry.rssi = advertisement.rssi + entry.connectable = advertisement.is_connectable else: - self.app.add_known_address(str(address)) - self.scan_results[entry_key] = ScanResult(address, address.address_type, ad_data, rssi, connectable) + self.app.add_known_address(str(advertisement.address)) + self.scan_results[entry_key] = ScanResult( + advertisement.address, + advertisement.address.address_type, + advertisement.data, + advertisement.rssi, + advertisement.is_connectable, + ) self.app.show_scan_results(self.scan_results) @@ -521,11 +1145,11 @@ class DeviceListener(Device.Listener, Connection.Listener): # ----------------------------------------------------------------------------- class ScanResult: def __init__(self, address, address_type, ad_data, rssi, connectable): - self.address = address + self.address = address self.address_type = address_type - self.ad_data = ad_data - self.rssi = rssi - self.connectable = connectable + self.ad_data = ad_data + self.rssi = rssi + self.connectable = connectable def to_display_string(self): address_type_string = ('P', 'R', 'PI', 'RI')[self.address_type] @@ -535,9 +1159,9 @@ class ScanResult: else: type_color = colors.cyan - name = self.ad_data.get(AdvertisingData.COMPLETE_LOCAL_NAME) + name = self.ad_data.get(AdvertisingData.COMPLETE_LOCAL_NAME, raw=True) if name is None: - name = self.ad_data.get(AdvertisingData.SHORTENED_LOCAL_NAME) + name = self.ad_data.get(AdvertisingData.SHORTENED_LOCAL_NAME, raw=True) if name: # Convert to string try: @@ -547,15 +1171,16 @@ class ScanResult: else: name = '' + # Remove any '/P' qualifier suffix from the address string + address_str = str(self.address).replace('/P', '') + # RSSI bar - blocks = ['', 'â–', 'â–Ž', 'â–', 'â–Œ', 'â–‹', 'â–Š', 'â–‰'] - bar_width = (self.rssi - DISPLAY_MIN_RSSI) / (DISPLAY_MAX_RSSI - DISPLAY_MIN_RSSI) - bar_width = min(max(bar_width, 0), 1) - bar_ticks = int(bar_width * DEFAULT_RSSI_BAR_WIDTH * 8) - bar_blocks = ('â–ˆ' * int(bar_ticks / 8)) + blocks[bar_ticks % 8] - bar_string = f'{self.rssi} {bar_blocks}' + bar_string = rssi_bar(self.rssi) bar_padding = ' ' * (DEFAULT_RSSI_BAR_WIDTH + 5 - len(bar_string)) - return f'{address_color(str(self.address))} [{type_color(address_type_string)}] {bar_string} {bar_padding} {name}' + return ( + f'{address_color(address_str)} [{type_color(address_type_string)}] ' + f'{bar_string} {bar_padding} {name}' + ) # ----------------------------------------------------------------------------- @@ -565,6 +1190,7 @@ class LogHandler(logging.Handler): def __init__(self, app): super().__init__() self.app = app + self.setFormatter(logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')) def emit(self, record): message = self.format(record) @@ -582,13 +1208,14 @@ def main(device_config, transport): if not os.path.isdir(BUMBLE_USER_DIR): os.mkdir(BUMBLE_USER_DIR) - # Create an instane of the app + # Create an instance of the app app = ConsoleApp() # Setup logging # logging.basicConfig(level = 'FATAL') # logging.basicConfig(level = 'DEBUG') root_logger = logging.getLogger() + root_logger.addHandler(LogHandler(app)) root_logger.setLevel(logging.DEBUG) @@ -598,4 +1225,4 @@ def main(device_config, transport): # ----------------------------------------------------------------------------- if __name__ == "__main__": - main() + main() # pylint: disable=no-value-for-parameter diff --git a/apps/controller_info.py b/apps/controller_info.py new file mode 100644 index 0000000..4707983 --- /dev/null +++ b/apps/controller_info.py @@ -0,0 +1,173 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import asyncio +import os +import logging +import click +from bumble.company_ids import COMPANY_IDENTIFIERS + +from bumble.colors import color +from bumble.core import name_or_number +from bumble.hci import ( + map_null_terminated_utf8_string, + HCI_SUCCESS, + HCI_LE_SUPPORTED_FEATURES_NAMES, + HCI_VERSION_NAMES, + LMP_VERSION_NAMES, + HCI_Command, + HCI_Command_Complete_Event, + HCI_Command_Status_Event, + HCI_READ_BD_ADDR_COMMAND, + HCI_Read_BD_ADDR_Command, + HCI_READ_LOCAL_NAME_COMMAND, + HCI_Read_Local_Name_Command, + HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND, + HCI_LE_Read_Maximum_Data_Length_Command, + HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_COMMAND, + HCI_LE_Read_Number_Of_Supported_Advertising_Sets_Command, + HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND, + HCI_LE_Read_Maximum_Advertising_Data_Length_Command, +) +from bumble.host import Host +from bumble.transport import open_transport_or_link + + +# ----------------------------------------------------------------------------- +def command_succeeded(response): + if isinstance(response, HCI_Command_Status_Event): + return response.status == HCI_SUCCESS + if isinstance(response, HCI_Command_Complete_Event): + return response.return_parameters.status == HCI_SUCCESS + return False + + +# ----------------------------------------------------------------------------- +async def get_classic_info(host): + if host.supports_command(HCI_READ_BD_ADDR_COMMAND): + response = await host.send_command(HCI_Read_BD_ADDR_Command()) + if command_succeeded(response): + print() + print( + color('Classic Address:', 'yellow'), response.return_parameters.bd_addr + ) + + if host.supports_command(HCI_READ_LOCAL_NAME_COMMAND): + response = await host.send_command(HCI_Read_Local_Name_Command()) + if command_succeeded(response): + print() + print( + color('Local Name:', 'yellow'), + map_null_terminated_utf8_string(response.return_parameters.local_name), + ) + + +# ----------------------------------------------------------------------------- +async def get_le_info(host): + print() + + if host.supports_command(HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_COMMAND): + response = await host.send_command( + HCI_LE_Read_Number_Of_Supported_Advertising_Sets_Command() + ) + if command_succeeded(response): + print( + color('LE Number Of Supported Advertising Sets:', 'yellow'), + response.return_parameters.num_supported_advertising_sets, + '\n', + ) + + if host.supports_command(HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND): + response = await host.send_command( + HCI_LE_Read_Maximum_Advertising_Data_Length_Command() + ) + if command_succeeded(response): + print( + color('LE Maximum Advertising Data Length:', 'yellow'), + response.return_parameters.max_advertising_data_length, + '\n', + ) + + if host.supports_command(HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND): + response = await host.send_command(HCI_LE_Read_Maximum_Data_Length_Command()) + if command_succeeded(response): + print( + color('Maximum Data Length:', 'yellow'), + ( + f'tx:{response.return_parameters.supported_max_tx_octets}/' + f'{response.return_parameters.supported_max_tx_time}, ' + f'rx:{response.return_parameters.supported_max_rx_octets}/' + f'{response.return_parameters.supported_max_rx_time}' + ), + '\n', + ) + + print(color('LE Features:', 'yellow')) + for feature in host.supported_le_features: + print(' ', name_or_number(HCI_LE_SUPPORTED_FEATURES_NAMES, feature)) + + +# ----------------------------------------------------------------------------- +async def async_main(transport): + print('<<< connecting to HCI...') + async with await open_transport_or_link(transport) as (hci_source, hci_sink): + print('<<< connected') + + host = Host(hci_source, hci_sink) + await host.reset() + + # Print version + print(color('Version:', 'yellow')) + print( + color(' Manufacturer: ', 'green'), + name_or_number(COMPANY_IDENTIFIERS, host.local_version.company_identifier), + ) + print( + color(' HCI Version: ', 'green'), + name_or_number(HCI_VERSION_NAMES, host.local_version.hci_version), + ) + print(color(' HCI Subversion:', 'green'), host.local_version.hci_subversion) + print( + color(' LMP Version: ', 'green'), + name_or_number(LMP_VERSION_NAMES, host.local_version.lmp_version), + ) + print(color(' LMP Subversion:', 'green'), host.local_version.lmp_subversion) + + # Get the Classic info + await get_classic_info(host) + + # Get the LE info + await get_le_info(host) + + # Print the list of commands supported by the controller + print() + print(color('Supported Commands:', 'yellow')) + for command in host.supported_commands: + print(' ', HCI_Command.command_name(command)) + + +# ----------------------------------------------------------------------------- +@click.command() +@click.argument('transport') +def main(transport): + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper()) + asyncio.run(async_main(transport)) + + +# ----------------------------------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/apps/controllers.py b/apps/controllers.py index 8e5f70e..ac6477e 100644 --- a/apps/controllers.py +++ b/apps/controllers.py @@ -28,11 +28,14 @@ from bumble.transport import open_transport_or_link # ----------------------------------------------------------------------------- async def async_main(): if len(sys.argv) != 3: - print('Usage: controllers.py <hci-transport-1> <hci-transport-2> [<hci-transport-3> ...]') + print( + 'Usage: controllers.py <hci-transport-1> <hci-transport-2> ' + '[<hci-transport-3> ...]' + ) print('example: python controllers.py pty:ble1 pty:ble2') return - # Create a loccal link to attach the controllers to + # Create a local link to attach the controllers to link = LocalLink() # Create a transport and controller for all requested names @@ -41,7 +44,12 @@ async def async_main(): for index, transport_name in enumerate(sys.argv[1:]): transport = await open_transport_or_link(transport_name) transports.append(transport) - controller = Controller(f'C{index}', host_source = transport.source, host_sink = transport.sink, link = link) + controller = Controller( + f'C{index}', + host_source=transport.source, + host_sink=transport.sink, + link=link, + ) controllers.append(controller) # Wait until the user interrupts @@ -54,7 +62,7 @@ async def async_main(): # ----------------------------------------------------------------------------- def main(): - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) asyncio.run(async_main()) diff --git a/apps/gatt_dump.py b/apps/gatt_dump.py index 02c3641..a3205c0 100644 --- a/apps/gatt_dump.py +++ b/apps/gatt_dump.py @@ -19,9 +19,9 @@ import asyncio import os import logging import click -from colors import color -from bumble.core import ProtocolError, TimeoutError +import bumble.core +from bumble.colors import color from bumble.device import Device, Peer from bumble.gatt import show_services from bumble.transport import open_transport_or_link @@ -49,9 +49,9 @@ async def dump_gatt_db(peer, done): try: value = await attribute.read_value() print(color(f'{value.hex()}', 'green')) - except ProtocolError as error: + except bumble.core.ProtocolError as error: print(color(error, 'red')) - except TimeoutError: + except bumble.core.TimeoutError: print(color('read timeout', 'red')) if done is not None: @@ -64,9 +64,13 @@ async def async_main(device_config, encrypt, transport, address_or_name): # Create a device if device_config: - device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink) + device = Device.from_config_file_with_hci( + device_config, hci_source, hci_sink + ) else: - device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink) + device = Device.with_hci( + 'Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink + ) await device.power_on() if address_or_name: @@ -81,7 +85,12 @@ async def async_main(device_config, encrypt, transport, address_or_name): else: # Wait for a connection done = asyncio.get_running_loop().create_future() - device.on('connection', lambda connection: asyncio.create_task(dump_gatt_db(Peer(connection), done))) + device.on( + 'connection', + lambda connection: asyncio.create_task( + dump_gatt_db(Peer(connection), done) + ), + ) await device.start_advertising(auto_restart=True) print(color('### Waiting for connection...', 'blue')) @@ -99,7 +108,7 @@ def main(device_config, encrypt, transport, address_or_name): Dump the GATT database on a remote device. If ADDRESS_OR_NAME is not specified, wait for an incoming connection. """ - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) asyncio.run(async_main(device_config, encrypt, transport, address_or_name)) diff --git a/apps/gg_bridge.py b/apps/gg_bridge.py index d524913..88ebdc5 100644 --- a/apps/gg_bridge.py +++ b/apps/gg_bridge.py @@ -17,13 +17,14 @@ # ----------------------------------------------------------------------------- import asyncio import os +import struct import logging import click -from colors import color +from bumble.colors import color from bumble.device import Device, Peer from bumble.core import AdvertisingData -from bumble.gatt import Service, Characteristic +from bumble.gatt import Service, Characteristic, CharacteristicValue from bumble.utils import AsyncRunner from bumble.transport import open_transport_or_link from bumble.hci import HCI_Constant @@ -32,24 +33,73 @@ from bumble.hci import HCI_Constant # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- -GG_GATTLINK_SERVICE_UUID = 'ABBAFF00-E56A-484C-B832-8B17CF6CBFE8' -GG_GATTLINK_RX_CHARACTERISTIC_UUID = 'ABBAFF01-E56A-484C-B832-8B17CF6CBFE8' -GG_GATTLINK_TX_CHARACTERISTIC_UUID = 'ABBAFF02-E56A-484C-B832-8B17CF6CBFE8' -GG_GATTLINK_L2CAP_CHANNEL_PSM_CHARACTERISTIC_UUID = 'ABBAFF03-E56A-484C-B832-8B17CF6CBFE8' +GG_GATTLINK_SERVICE_UUID = 'ABBAFF00-E56A-484C-B832-8B17CF6CBFE8' +GG_GATTLINK_RX_CHARACTERISTIC_UUID = 'ABBAFF01-E56A-484C-B832-8B17CF6CBFE8' +GG_GATTLINK_TX_CHARACTERISTIC_UUID = 'ABBAFF02-E56A-484C-B832-8B17CF6CBFE8' +GG_GATTLINK_L2CAP_CHANNEL_PSM_CHARACTERISTIC_UUID = ( + 'ABBAFF03-E56A-484C-B832-8B17CF6CBFE8' +) GG_PREFERRED_MTU = 256 # ----------------------------------------------------------------------------- -class GattlinkHubBridge(Device.Listener): +class GattlinkL2capEndpoint: def __init__(self): - self.peer = None - self.rx_socket = None - self.tx_socket = None + self.l2cap_channel = None + self.l2cap_packet = b'' + self.l2cap_packet_size = 0 + + # Called when an L2CAP SDU has been received + def on_coc_sdu(self, sdu): + print(color(f'<<< [L2CAP SDU]: {len(sdu)} bytes', 'cyan')) + while len(sdu): + if self.l2cap_packet_size == 0: + # Expect a new packet + self.l2cap_packet_size = sdu[0] + 1 + sdu = sdu[1:] + else: + bytes_needed = self.l2cap_packet_size - len(self.l2cap_packet) + chunk = min(bytes_needed, len(sdu)) + self.l2cap_packet += sdu[:chunk] + sdu = sdu[chunk:] + if len(self.l2cap_packet) == self.l2cap_packet_size: + self.on_l2cap_packet(self.l2cap_packet) + self.l2cap_packet = b'' + self.l2cap_packet_size = 0 + + +# ----------------------------------------------------------------------------- +class GattlinkHubBridge(GattlinkL2capEndpoint, Device.Listener): + def __init__(self, device, peer_address): + super().__init__() + self.device = device + self.peer_address = peer_address + self.peer = None + self.tx_socket = None self.rx_characteristic = None self.tx_characteristic = None + self.l2cap_psm_characteristic = None + + device.listener = self + + async def start(self): + # Connect to the peer + print(f'=== Connecting to {self.peer_address}...') + await self.device.connect(self.peer_address) + + async def connect_l2cap(self, psm): + print(color(f'### Connecting with L2CAP on PSM = {psm}', 'yellow')) + try: + self.l2cap_channel = await self.peer.connection.open_l2cap_channel(psm) + print(color('*** Connected', 'yellow'), self.l2cap_channel) + self.l2cap_channel.sink = self.on_coc_sdu + + except Exception as error: + print(color(f'!!! Connection failed: {error}', 'red')) @AsyncRunner.run_in_task() + # pylint: disable=invalid-overridden-method async def on_connection(self, connection): print(f'=== Connected to {connection}') self.peer = Peer(connection) @@ -80,115 +130,221 @@ class GattlinkHubBridge(Device.Listener): self.rx_characteristic = characteristic elif characteristic.uuid == GG_GATTLINK_TX_CHARACTERISTIC_UUID: self.tx_characteristic = characteristic + elif ( + characteristic.uuid == GG_GATTLINK_L2CAP_CHANNEL_PSM_CHARACTERISTIC_UUID + ): + self.l2cap_psm_characteristic = characteristic print('RX:', self.rx_characteristic) print('TX:', self.tx_characteristic) - - # Subscribe to TX - if self.tx_characteristic: + print('PSM:', self.l2cap_psm_characteristic) + + if self.l2cap_psm_characteristic: + # Subscribe to and then read the PSM value + await self.peer.subscribe( + self.l2cap_psm_characteristic, self.on_l2cap_psm_received + ) + psm_bytes = await self.peer.read_value(self.l2cap_psm_characteristic) + psm = struct.unpack('<H', psm_bytes)[0] + await self.connect_l2cap(psm) + elif self.tx_characteristic: + # Subscribe to TX await self.peer.subscribe(self.tx_characteristic, self.on_tx_received) print(color('=== Subscribed to Gattlink TX', 'yellow')) else: - print(color('!!! Gattlink TX not found', 'red')) + print(color('!!! No Gattlink TX or PSM found', 'red')) def on_connection_failure(self, error): print(color(f'!!! Connection failed: {error}')) def on_disconnection(self, reason): - print(color(f'!!! Disconnected from {self.peer}, reason={HCI_Constant.error_name(reason)}', 'red')) + print( + color( + f'!!! Disconnected from {self.peer}, ' + f'reason={HCI_Constant.error_name(reason)}', + 'red', + ) + ) self.tx_characteristic = None self.rx_characteristic = None self.peer = None + # Called when an L2CAP packet has been received + def on_l2cap_packet(self, packet): + print(color(f'<<< [L2CAP PACKET]: {len(packet)} bytes', 'cyan')) + print(color('>>> [UDP]', 'magenta')) + self.tx_socket.sendto(packet) + # Called by the GATT client when a notification is received def on_tx_received(self, value): - print(color('>>> TX:', 'magenta'), value.hex()) + print(color(f'<<< [GATT TX]: {len(value)} bytes', 'cyan')) if self.tx_socket: + print(color('>>> [UDP]', 'magenta')) self.tx_socket.sendto(value) # Called by asyncio when the UDP socket is created + def on_l2cap_psm_received(self, value): + psm = struct.unpack('<H', value)[0] + asyncio.create_task(self.connect_l2cap(psm)) + + # Called by asyncio when the UDP socket is created def connection_made(self, transport): pass # Called by asyncio when a UDP datagram is received - def datagram_received(self, data, address): - print(color('<<< RX:', 'magenta'), data.hex()) - - # TODO: use a queue instead of creating a task everytime - if self.peer and self.rx_characteristic: + def datagram_received(self, data, _address): + print(color(f'<<< [UDP]: {len(data)} bytes', 'green')) + + if self.l2cap_channel: + print(color('>>> [L2CAP]', 'yellow')) + self.l2cap_channel.write(bytes([len(data) - 1]) + data) + elif self.peer and self.rx_characteristic: + print(color('>>> [GATT RX]', 'yellow')) asyncio.create_task(self.peer.write_value(self.rx_characteristic, data)) # ----------------------------------------------------------------------------- -class GattlinkNodeBridge(Device.Listener): - def __init__(self): - self.peer = None - self.rx_socket = None +class GattlinkNodeBridge(GattlinkL2capEndpoint, Device.Listener): + def __init__(self, device): + super().__init__() + self.device = device + self.peer = None self.tx_socket = None + self.tx_subscriber = None + self.rx_characteristic = None + self.transport = None + + # Register as a listener + device.listener = self + + # Listen for incoming L2CAP CoC connections + psm = 0xFB + device.register_l2cap_channel_server(0xFB, self.on_coc) + print(f'### Listening for CoC connection on PSM {psm}') + + # Setup the Gattlink service + self.rx_characteristic = Characteristic( + GG_GATTLINK_RX_CHARACTERISTIC_UUID, + Characteristic.WRITE_WITHOUT_RESPONSE, + Characteristic.WRITEABLE, + CharacteristicValue(write=self.on_rx_write), + ) + self.tx_characteristic = Characteristic( + GG_GATTLINK_TX_CHARACTERISTIC_UUID, + Characteristic.Properties.NOTIFY, + Characteristic.READABLE, + ) + self.tx_characteristic.on('subscription', self.on_tx_subscription) + self.psm_characteristic = Characteristic( + GG_GATTLINK_L2CAP_CHANNEL_PSM_CHARACTERISTIC_UUID, + Characteristic.Properties.READ | Characteristic.Properties.NOTIFY, + Characteristic.READABLE, + bytes([psm, 0]), + ) + gattlink_service = Service( + GG_GATTLINK_SERVICE_UUID, + [self.rx_characteristic, self.tx_characteristic, self.psm_characteristic], + ) + device.add_services([gattlink_service]) + device.advertising_data = bytes( + AdvertisingData( + [ + (AdvertisingData.COMPLETE_LOCAL_NAME, bytes('Bumble GG', 'utf-8')), + ( + AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, + bytes( + reversed(bytes.fromhex('ABBAFF00E56A484CB8328B17CF6CBFE8')) + ), + ), + ] + ) + ) + + async def start(self): + await self.device.start_advertising() # Called by asyncio when the UDP socket is created def connection_made(self, transport): - pass + self.transport = transport # Called by asyncio when a UDP datagram is received - def datagram_received(self, data, address): - print(color('<<< RX:', 'magenta'), data.hex()) + def datagram_received(self, data, _address): + print(color(f'<<< [UDP]: {len(data)} bytes', 'green')) + + if self.l2cap_channel: + print(color('>>> [L2CAP]', 'yellow')) + self.l2cap_channel.write(bytes([len(data) - 1]) + data) + elif self.tx_subscriber: + print(color('>>> [GATT TX]', 'yellow')) + self.tx_characteristic.value = data + asyncio.create_task(self.device.notify_subscribers(self.tx_characteristic)) + + # Called when a write to the RX characteristic has been received + def on_rx_write(self, _connection, data): + print(color(f'<<< [GATT RX]: {len(data)} bytes', 'cyan')) + print(color('>>> [UDP]', 'magenta')) + self.tx_socket.sendto(data) + + # Called when the subscription to the TX characteristic has changed + def on_tx_subscription(self, peer, enabled): + print( + f'### [GATT TX] subscription from {peer}: ' + f'{"enabled" if enabled else "disabled"}' + ) + if enabled: + self.tx_subscriber = peer + else: + self.tx_subscriber = None - # TODO: use a queue instead of creating a task everytime - if self.peer and self.rx_characteristic: - asyncio.create_task(self.peer.write_value(self.rx_characteristic, data)) + # Called when an L2CAP packet is received + def on_l2cap_packet(self, packet): + print(color(f'<<< [L2CAP PACKET]: {len(packet)} bytes', 'cyan')) + print(color('>>> [UDP]', 'magenta')) + self.tx_socket.sendto(packet) + + # Called when a new connection is established + def on_coc(self, channel): + print('*** CoC Connection', channel) + self.l2cap_channel = channel + channel.sink = self.on_coc_sdu # ----------------------------------------------------------------------------- -async def run(hci_transport, device_address, send_host, send_port, receive_host, receive_port): +async def run( + hci_transport, + device_address, + role_or_peer_address, + send_host, + send_port, + receive_host, + receive_port, +): print('<<< connecting to HCI...') async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink): print('<<< connected') # Instantiate a bridge object - bridge = GattlinkNodeBridge() + device = Device.with_hci('Bumble GG', device_address, hci_source, hci_sink) + + # Instantiate a bridge object + if role_or_peer_address == 'node': + bridge = GattlinkNodeBridge(device) + else: + bridge = GattlinkHubBridge(device, role_or_peer_address) # Create a UDP to RX bridge (receive from UDP, send to RX) loop = asyncio.get_running_loop() await loop.create_datagram_endpoint( - lambda: bridge, - local_addr=(receive_host, receive_port) + lambda: bridge, local_addr=(receive_host, receive_port) ) # Create a UDP to TX bridge (receive from TX, send to UDP) bridge.tx_socket, _ = await loop.create_datagram_endpoint( - lambda: asyncio.DatagramProtocol(), - remote_addr=(send_host, send_port) + asyncio.DatagramProtocol, + remote_addr=(send_host, send_port), ) - # Create a device to manage the host, with a custom listener - device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink) - device.listener = bridge await device.power_on() - - # Connect to the peer - # print(f'=== Connecting to {device_address}...') - # await device.connect(device_address) - - # TODO move to class - gattlink_service = Service( - GG_GATTLINK_SERVICE_UUID, - [ - Characteristic( - GG_GATTLINK_L2CAP_CHANNEL_PSM_CHARACTERISTIC_UUID, - Characteristic.READ, - Characteristic.READABLE, - bytes([193, 0]) - ) - ] - ) - device.add_services([gattlink_service]) - device.advertising_data = bytes( - AdvertisingData([ - (AdvertisingData.COMPLETE_LOCAL_NAME, bytes('Bumble GG', 'utf-8')), - (AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, bytes(reversed(bytes.fromhex('ABBAFF00E56A484CB8328B17CF6CBFE8')))) - ]) - ) - await device.start_advertising() + await bridge.start() # Wait until the source terminates await hci_source.wait_for_termination() @@ -197,15 +353,44 @@ async def run(hci_transport, device_address, send_host, send_port, receive_host, @click.command() @click.argument('hci_transport') @click.argument('device_address') -@click.option('-sh', '--send-host', type=str, default='127.0.0.1', help='UDP host to send to') +@click.argument('role_or_peer_address') +@click.option( + '-sh', '--send-host', type=str, default='127.0.0.1', help='UDP host to send to' +) @click.option('-sp', '--send-port', type=int, default=9001, help='UDP port to send to') -@click.option('-rh', '--receive-host', type=str, default='127.0.0.1', help='UDP host to receive on') -@click.option('-rp', '--receive-port', type=int, default=9000, help='UDP port to receive on') -def main(hci_transport, device_address, send_host, send_port, receive_host, receive_port): - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) - asyncio.run(run(hci_transport, device_address, send_host, send_port, receive_host, receive_port)) +@click.option( + '-rh', + '--receive-host', + type=str, + default='127.0.0.1', + help='UDP host to receive on', +) +@click.option( + '-rp', '--receive-port', type=int, default=9000, help='UDP port to receive on' +) +def main( + hci_transport, + device_address, + role_or_peer_address, + send_host, + send_port, + receive_host, + receive_port, +): + asyncio.run( + run( + hci_transport, + device_address, + role_or_peer_address, + send_host, + send_port, + receive_host, + receive_port, + ) + ) # ----------------------------------------------------------------------------- +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper()) if __name__ == '__main__': main() diff --git a/apps/hci_bridge.py b/apps/hci_bridge.py index 0680f52..1d1f9a1 100644 --- a/apps/hci_bridge.py +++ b/apps/hci_bridge.py @@ -34,16 +34,29 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- async def async_main(): if len(sys.argv) < 3: - print('Usage: hci_bridge.py <host-transport-spec> <controller-transport-spec> [command-short-circuit-list]') - print('example: python hci_bridge.py udp:0.0.0.0:9000,127.0.0.1:9001 serial:/dev/tty.usbmodem0006839912171,1000000 0x3f:0x0070,0x3f:0x0074,0x3f:0x0077,0x3f:0x0078') + print( + 'Usage: hci_bridge.py <host-transport-spec> <controller-transport-spec> ' + '[command-short-circuit-list]' + ) + print( + 'example: python hci_bridge.py udp:0.0.0.0:9000,127.0.0.1:9001 ' + 'serial:/dev/tty.usbmodem0006839912171,1000000 ' + '0x3f:0x0070,0x3f:0x0074,0x3f:0x0077,0x3f:0x0078' + ) return print('>>> connecting to HCI...') - async with await transport.open_transport_or_link(sys.argv[1]) as (hci_host_source, hci_host_sink): + async with await transport.open_transport_or_link(sys.argv[1]) as ( + hci_host_source, + hci_host_sink, + ): print('>>> connected') print('>>> connecting to HCI...') - async with await transport.open_transport_or_link(sys.argv[2]) as (hci_controller_source, hci_controller_sink): + async with await transport.open_transport_or_link(sys.argv[2]) as ( + hci_controller_source, + hci_controller_sink, + ): print('>>> connected') command_short_circuits = [] @@ -51,36 +64,43 @@ async def async_main(): for op_code_str in sys.argv[3].split(','): if ':' in op_code_str: ogf, ocf = op_code_str.split(':') - command_short_circuits.append(hci.hci_command_op_code(int(ogf, 16), int(ocf, 16))) + command_short_circuits.append( + hci.hci_command_op_code(int(ogf, 16), int(ocf, 16)) + ) else: command_short_circuits.append(int(op_code_str, 16)) def host_to_controller_filter(hci_packet): - if hci_packet.hci_packet_type == hci.HCI_COMMAND_PACKET and hci_packet.op_code in command_short_circuits: + if ( + hci_packet.hci_packet_type == hci.HCI_COMMAND_PACKET + and hci_packet.op_code in command_short_circuits + ): # Respond with a success response logger.debug('short-circuiting packet') response = hci.HCI_Command_Complete_Event( - num_hci_command_packets = 1, - command_opcode = hci_packet.op_code, - return_parameters = bytes([hci.HCI_SUCCESS]) + num_hci_command_packets=1, + command_opcode=hci_packet.op_code, + return_parameters=bytes([hci.HCI_SUCCESS]), ) # Return a packet with 'respond to sender' set to True return (response.to_bytes(), True) + return None + _ = HCI_Bridge( hci_host_source, hci_host_sink, hci_controller_source, hci_controller_sink, host_to_controller_filter, - None + None, ) await asyncio.get_running_loop().create_future() # ----------------------------------------------------------------------------- def main(): - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) asyncio.run(async_main()) diff --git a/apps/l2cap_bridge.py b/apps/l2cap_bridge.py new file mode 100644 index 0000000..17623e4 --- /dev/null +++ b/apps/l2cap_bridge.py @@ -0,0 +1,350 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import asyncio +import logging +import os +import click + +from bumble.colors import color +from bumble.transport import open_transport_or_link +from bumble.device import Device +from bumble.utils import FlowControlAsyncPipe +from bumble.hci import HCI_Constant + + +# ----------------------------------------------------------------------------- +class ServerBridge: + """ + L2CAP CoC server bridge: waits for a peer to connect an L2CAP CoC channel + on a specified PSM. When the connection is made, the bridge connects a TCP + socket to a remote host and bridges the data in both directions, with flow + control. + When the L2CAP CoC channel is closed, the bridge disconnects the TCP socket + and waits for a new L2CAP CoC channel to be connected. + When the TCP connection is closed by the TCP server, XXXX + """ + + def __init__(self, psm, max_credits, mtu, mps, tcp_host, tcp_port): + self.psm = psm + self.max_credits = max_credits + self.mtu = mtu + self.mps = mps + self.tcp_host = tcp_host + self.tcp_port = tcp_port + + async def start(self, device): + # Listen for incoming L2CAP CoC connections + device.register_l2cap_channel_server( + psm=self.psm, + server=self.on_coc, + max_credits=self.max_credits, + mtu=self.mtu, + mps=self.mps, + ) + print(color(f'### Listening for CoC connection on PSM {self.psm}', 'yellow')) + + def on_ble_connection(connection): + def on_ble_disconnection(reason): + print( + color('@@@ Bluetooth disconnection:', 'red'), + HCI_Constant.error_name(reason), + ) + + print(color('@@@ Bluetooth connection:', 'green'), connection) + connection.on('disconnection', on_ble_disconnection) + + device.on('connection', on_ble_connection) + + await device.start_advertising(auto_restart=True) + + # Called when a new L2CAP connection is established + def on_coc(self, l2cap_channel): + print(color('*** L2CAP channel:', 'cyan'), l2cap_channel) + + class Pipe: + def __init__(self, bridge, l2cap_channel): + self.bridge = bridge + self.tcp_transport = None + self.l2cap_channel = l2cap_channel + + l2cap_channel.on('close', self.on_l2cap_close) + l2cap_channel.sink = self.on_coc_sdu + + async def connect_to_tcp(self): + # Connect to the TCP server + print( + color( + f'### Connecting to TCP {self.bridge.tcp_host}:' + f'{self.bridge.tcp_port}...', + 'yellow', + ) + ) + + class TcpClientProtocol(asyncio.Protocol): + def __init__(self, pipe): + self.pipe = pipe + + def connection_lost(self, exc): + print(color(f'!!! TCP connection lost: {exc}', 'red')) + if self.pipe.l2cap_channel is not None: + asyncio.create_task(self.pipe.l2cap_channel.disconnect()) + + def data_received(self, data): + print(f'<<< Received on TCP: {len(data)}') + self.pipe.l2cap_channel.write(data) + + try: + ( + self.tcp_transport, + _, + ) = await asyncio.get_running_loop().create_connection( + lambda: TcpClientProtocol(self), + host=self.bridge.tcp_host, + port=self.bridge.tcp_port, + ) + print(color('### Connected', 'green')) + except Exception as error: + print(color(f'!!! Connection failed: {error}', 'red')) + await self.l2cap_channel.disconnect() + + def on_l2cap_close(self): + self.l2cap_channel = None + if self.tcp_transport is not None: + self.tcp_transport.close() + + def on_coc_sdu(self, sdu): + print(color(f'<<< [L2CAP SDU]: {len(sdu)} bytes', 'cyan')) + if self.tcp_transport is None: + print(color('!!! TCP socket not open, dropping', 'red')) + return + self.tcp_transport.write(sdu) + + pipe = Pipe(self, l2cap_channel) + + asyncio.create_task(pipe.connect_to_tcp()) + + +# ----------------------------------------------------------------------------- +class ClientBridge: + """ + L2CAP CoC client bridge: connects to a BLE device, then waits for an inbound + TCP connection on a specified port number. When a TCP client connects, an + L2CAP CoC channel connection to the BLE device is established, and the data + is bridged in both directions, with flow control. + When the TCP connection is closed by the client, the L2CAP CoC channel is + disconnected, but the connection to the BLE device remains, ready for a new + TCP client to connect. + When the L2CAP CoC channel is closed, XXXX + """ + + READ_CHUNK_SIZE = 4096 + + def __init__(self, psm, max_credits, mtu, mps, address, tcp_host, tcp_port): + self.psm = psm + self.max_credits = max_credits + self.mtu = mtu + self.mps = mps + self.address = address + self.tcp_host = tcp_host + self.tcp_port = tcp_port + + async def start(self, device): + print(color(f'### Connecting to {self.address}...', 'yellow')) + connection = await device.connect(self.address) + print(color('### Connected', 'green')) + + # Called when the BLE connection is disconnected + def on_ble_disconnection(reason): + print( + color('@@@ Bluetooth disconnection:', 'red'), + HCI_Constant.error_name(reason), + ) + + connection.on('disconnection', on_ble_disconnection) + + # Called when a TCP connection is established + async def on_tcp_connection(reader, writer): + peer_name = writer.get_extra_info('peer_name') + print(color(f'<<< TCP connection from {peer_name}', 'magenta')) + + def on_coc_sdu(sdu): + print(color(f'<<< [L2CAP SDU]: {len(sdu)} bytes', 'cyan')) + l2cap_to_tcp_pipe.write(sdu) + + def on_l2cap_close(): + print(color('*** L2CAP channel closed', 'red')) + l2cap_to_tcp_pipe.stop() + writer.close() + + # Connect a new L2CAP channel + print(color(f'>>> Opening L2CAP channel on PSM = {self.psm}', 'yellow')) + try: + l2cap_channel = await connection.open_l2cap_channel( + psm=self.psm, + max_credits=self.max_credits, + mtu=self.mtu, + mps=self.mps, + ) + print(color('*** L2CAP channel:', 'cyan'), l2cap_channel) + except Exception as error: + print(color(f'!!! Connection failed: {error}', 'red')) + writer.close() + return + + l2cap_channel.sink = on_coc_sdu + l2cap_channel.on('close', on_l2cap_close) + + # Start a flow control pipe from L2CAP to TCP + l2cap_to_tcp_pipe = FlowControlAsyncPipe( + l2cap_channel.pause_reading, + l2cap_channel.resume_reading, + writer.write, + writer.drain, + ) + l2cap_to_tcp_pipe.start() + + # Pipe data from TCP to L2CAP + while True: + try: + data = await reader.read(self.READ_CHUNK_SIZE) + + if len(data) == 0: + print(color('!!! End of stream', 'red')) + await l2cap_channel.disconnect() + return + + print(color(f'<<< [TCP DATA]: {len(data)} bytes', 'blue')) + l2cap_channel.write(data) + await l2cap_channel.drain() + except Exception as error: + print(f'!!! Exception: {error}') + break + + writer.close() + print(color('~~~ Bye bye', 'magenta')) + + await asyncio.start_server( + on_tcp_connection, + host=self.tcp_host if self.tcp_host != '_' else None, + port=self.tcp_port, + ) + print( + color( + f'### Listening for TCP connections on port {self.tcp_port}', 'magenta' + ) + ) + + +# ----------------------------------------------------------------------------- +async def run(device_config, hci_transport, bridge): + print('<<< connecting to HCI...') + async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink): + print('<<< connected') + + device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink) + + # Let's go + await device.power_on() + await bridge.start(device) + + # Wait until the transport terminates + await hci_source.wait_for_termination() + + +# ----------------------------------------------------------------------------- +@click.group() +@click.pass_context +@click.option('--device-config', help='Device configuration file', required=True) +@click.option('--hci-transport', help='HCI transport', required=True) +@click.option('--psm', help='PSM for L2CAP CoC', type=int, default=1234) +@click.option( + '--l2cap-coc-max-credits', + help='Maximum L2CAP CoC Credits', + type=click.IntRange(1, 65535), + default=128, +) +@click.option( + '--l2cap-coc-mtu', + help='L2CAP CoC MTU', + type=click.IntRange(23, 65535), + default=1022, +) +@click.option( + '--l2cap-coc-mps', + help='L2CAP CoC MPS', + type=click.IntRange(23, 65533), + default=1024, +) +def cli( + context, + device_config, + hci_transport, + psm, + l2cap_coc_max_credits, + l2cap_coc_mtu, + l2cap_coc_mps, +): + context.ensure_object(dict) + context.obj['device_config'] = device_config + context.obj['hci_transport'] = hci_transport + context.obj['psm'] = psm + context.obj['max_credits'] = l2cap_coc_max_credits + context.obj['mtu'] = l2cap_coc_mtu + context.obj['mps'] = l2cap_coc_mps + + +# ----------------------------------------------------------------------------- +@cli.command() +@click.pass_context +@click.option('--tcp-host', help='TCP host', default='localhost') +@click.option('--tcp-port', help='TCP port', default=9544) +def server(context, tcp_host, tcp_port): + bridge = ServerBridge( + context.obj['psm'], + context.obj['max_credits'], + context.obj['mtu'], + context.obj['mps'], + tcp_host, + tcp_port, + ) + asyncio.run(run(context.obj['device_config'], context.obj['hci_transport'], bridge)) + + +# ----------------------------------------------------------------------------- +@cli.command() +@click.pass_context +@click.argument('bluetooth-address') +@click.option('--tcp-host', help='TCP host', default='_') +@click.option('--tcp-port', help='TCP port', default=9543) +def client(context, bluetooth_address, tcp_host, tcp_port): + bridge = ClientBridge( + context.obj['psm'], + context.obj['max_credits'], + context.obj['mtu'], + context.obj['mps'], + bluetooth_address, + tcp_host, + tcp_port, + ) + asyncio.run(run(context.obj['device_config'], context.obj['hci_transport'], bridge)) + + +# ----------------------------------------------------------------------------- +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper()) +if __name__ == '__main__': + cli(obj={}) # pylint: disable=no-value-for-parameter diff --git a/apps/link_relay/link_relay.py b/apps/link_relay/link_relay.py index c979ea6..6036fa0 100644 --- a/apps/link_relay/link_relay.py +++ b/apps/link_relay/link_relay.py @@ -16,7 +16,6 @@ # Imports # ---------------------------------------------------------------------------- import sys -import websockets import logging import json import asyncio @@ -24,7 +23,9 @@ import argparse import uuid import os from urllib.parse import urlparse -from colors import color +import websockets + +from bumble.colors import color # ----------------------------------------------------------------------------- # Logging @@ -65,9 +66,9 @@ class Connection: """ def __init__(self, room, websocket): - self.room = room + self.room = room self.websocket = websocket - self.address = str(uuid.uuid4()) + self.address = str(uuid.uuid4()) async def send_message(self, message): try: @@ -98,7 +99,11 @@ class Connection: self.address = address def __str__(self): - return f'Connection(address="{self.address}", client={self.websocket.remote_address[0]}:{self.websocket.remote_address[1]})' + return ( + f'Connection(address="{self.address}", ' + f'client={self.websocket.remote_address[0]}:' + f'{self.websocket.remote_address[1]})' + ) # ---------------------------------------------------------------------------- @@ -110,9 +115,9 @@ class Room: """ def __init__(self, relay, name): - self.relay = relay - self.name = name - self.observers = [] + self.relay = relay + self.name = name + self.observers = [] self.connections = [] async def add_connection(self, connection): @@ -139,13 +144,15 @@ class Room: # Parse the message to decide how to handle it if message.startswith('@'): - # This is a targetted message - await self.on_targetted_message(connection, message) + # This is a targeted message + await self.on_targeted_message(connection, message) elif message.startswith('/'): # This is an RPC request await self.on_rpc_request(connection, message) else: - await connection.send_message(f'result:{error_to_json("error: invalid message")}') + await connection.send_message( + f'result:{error_to_json("error: invalid message")}' + ) async def broadcast_message(self, sender, message): ''' @@ -155,7 +162,9 @@ class Room: async def on_rpc_request(self, connection, message): command, *params = message.split(' ', 1) - if handler := getattr(self, f'on_{command[1:].lower().replace("-","_")}_command', None): + if handler := getattr( + self, f'on_{command[1:].lower().replace("-","_")}_command', None + ): try: result = await handler(connection, params) except Exception as error: @@ -165,7 +174,7 @@ class Room: await connection.send_message(result or 'result:{}') - async def on_targetted_message(self, connection, message): + async def on_targeted_message(self, connection, message): target, *payload = message.split(' ', 1) if not payload: return error_to_json('missing arguments') @@ -174,7 +183,8 @@ class Room: # Determine what targets to send to if target == '*': - # Send to all connections in the room except the connection from which the message was received + # Send to all connections in the room except the connection from which the + # message was received connections = [c for c in self.connections if c != connection] else: connections = self.find_connections_by_address(target) @@ -192,7 +202,9 @@ class Room: current_address = connection.address new_address = params[0] connection.set_address(new_address) - await self.broadcast_message(connection, f'address-changed:from={current_address},to={new_address}') + await self.broadcast_message( + connection, f'address-changed:from={current_address},to={new_address}' + ) # ---------------------------------------------------------------------------- @@ -210,9 +222,10 @@ class Relay: def start(self): logger.info(f'Starting Relay on port {self.port}') + # pylint: disable-next=no-member return websockets.serve(self.serve, '0.0.0.0', self.port, ping_interval=None) - async def serve_as_controller(connection): + async def serve_as_controller(self, connection): pass async def serve(self, websocket, path): @@ -246,24 +259,24 @@ def main(): print('ERROR: Python 3.6.1 or higher is required') sys.exit(1) - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) # Parse arguments arg_parser = argparse.ArgumentParser(description='Bumble Link Relay') arg_parser.add_argument('--log-level', default='INFO', help='logger level') arg_parser.add_argument('--log-config', help='logger config file (YAML)') - arg_parser.add_argument('--port', - type = int, - default = DEFAULT_RELAY_PORT, - help = 'Port to listen on') + arg_parser.add_argument( + '--port', type=int, default=DEFAULT_RELAY_PORT, help='Port to listen on' + ) args = arg_parser.parse_args() # Setup logger if args.log_config: - from logging import config + from logging import config # pylint: disable=import-outside-toplevel + config.fileConfig(args.log_config) else: - logging.basicConfig(level = getattr(logging, args.log_level.upper())) + logging.basicConfig(level=getattr(logging, args.log_level.upper())) # Start a relay relay = Relay(args.port) diff --git a/apps/pair.py b/apps/pair.py index 7f89629..a7844fe 100644 --- a/apps/pair.py +++ b/apps/pair.py @@ -19,12 +19,12 @@ import asyncio import os import logging import click -import aioconsole -from colors import color +from prompt_toolkit.shortcuts import PromptSession +from bumble.colors import color from bumble.device import Device, Peer from bumble.transport import open_transport_or_link -from bumble.smp import PairingDelegate, PairingConfig +from bumble.pairing import PairingDelegate, PairingConfig from bumble.smp import error_name as smp_error_name from bumble.keys import JsonKeyStore from bumble.core import ProtocolError @@ -33,30 +33,57 @@ from bumble.gatt import ( GATT_GENERIC_ACCESS_SERVICE, Service, Characteristic, - CharacteristicValue + CharacteristicValue, ) from bumble.att import ( ATT_Error, ATT_INSUFFICIENT_AUTHENTICATION_ERROR, - ATT_INSUFFICIENT_ENCRYPTION_ERROR + ATT_INSUFFICIENT_ENCRYPTION_ERROR, ) # ----------------------------------------------------------------------------- +class Waiter: + instance = None + + def __init__(self): + self.done = asyncio.get_running_loop().create_future() + + def terminate(self): + self.done.set_result(None) + + async def wait_until_terminated(self): + return await self.done + + +# ----------------------------------------------------------------------------- class Delegate(PairingDelegate): - def __init__(self, mode, connection, capability_string, prompt): - super().__init__({ - 'keyboard': PairingDelegate.KEYBOARD_INPUT_ONLY, - 'display': PairingDelegate.DISPLAY_OUTPUT_ONLY, - 'display+keyboard': PairingDelegate.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT, - 'display+yes/no': PairingDelegate.DISPLAY_OUTPUT_AND_YES_NO_INPUT, - 'none': PairingDelegate.NO_OUTPUT_NO_INPUT - }[capability_string.lower()]) - - self.mode = mode - self.peer = Peer(connection) + def __init__(self, mode, connection, capability_string, do_prompt): + super().__init__( + { + 'keyboard': PairingDelegate.KEYBOARD_INPUT_ONLY, + 'display': PairingDelegate.DISPLAY_OUTPUT_ONLY, + 'display+keyboard': PairingDelegate.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT, + 'display+yes/no': PairingDelegate.DISPLAY_OUTPUT_AND_YES_NO_INPUT, + 'none': PairingDelegate.NO_OUTPUT_NO_INPUT, + }[capability_string.lower()] + ) + + self.mode = mode + self.peer = Peer(connection) self.peer_name = None - self.prompt = prompt + self.do_prompt = do_prompt + + def print(self, message): + print(color(message, 'yellow')) + + async def prompt(self, message): + # Wait a bit to allow some of the log lines to print before we prompt + await asyncio.sleep(1) + + session = PromptSession(message) + response = await session.prompt_async() + return response.lower().strip() async def update_peer_name(self): if self.peer_name is not None: @@ -71,87 +98,83 @@ class Delegate(PairingDelegate): self.peer_name = '[?]' async def accept(self): - if self.prompt: + if self.do_prompt: await self.update_peer_name() - # Wait a bit to allow some of the log lines to print before we prompt - await asyncio.sleep(1) - # Prompt for acceptance - print(color('###-----------------------------------', 'yellow')) - print(color(f'### Pairing request from {self.peer_name}', 'yellow')) - print(color('###-----------------------------------', 'yellow')) + self.print('###-----------------------------------') + self.print(f'### Pairing request from {self.peer_name}') + self.print('###-----------------------------------') while True: - response = await aioconsole.ainput(color('>>> Accept? ', 'yellow')) - response = response.lower().strip() + response = await self.prompt('>>> Accept? ') + if response == 'yes': return True - elif response == 'no': + + if response == 'no': return False - else: - # Accept silently - return True + + # Accept silently + return True async def compare_numbers(self, number, digits): await self.update_peer_name() - # Wait a bit to allow some of the log lines to print before we prompt - await asyncio.sleep(1) - # Prompt for a numeric comparison - print(color('###-----------------------------------', 'yellow')) - print(color(f'### Pairing with {self.peer_name}', 'yellow')) - print(color('###-----------------------------------', 'yellow')) + self.print('###-----------------------------------') + self.print(f'### Pairing with {self.peer_name}') + self.print('###-----------------------------------') while True: - response = await aioconsole.ainput(color(f'>>> Does the other device display {number:0{digits}}? ', 'yellow')) - response = response.lower().strip() + response = await self.prompt( + f'>>> Does the other device display {number:0{digits}}? ' + ) + if response == 'yes': return True - elif response == 'no': + + if response == 'no': return False async def get_number(self): await self.update_peer_name() - # Wait a bit to allow some of the log lines to print before we prompt - await asyncio.sleep(1) - # Prompt for a PIN while True: try: - print(color('###-----------------------------------', 'yellow')) - print(color(f'### Pairing with {self.peer_name}', 'yellow')) - print(color('###-----------------------------------', 'yellow')) - return int(await aioconsole.ainput(color('>>> Enter PIN: ', 'yellow'))) + self.print('###-----------------------------------') + self.print(f'### Pairing with {self.peer_name}') + self.print('###-----------------------------------') + return int(await self.prompt('>>> Enter PIN: ')) except ValueError: pass async def display_number(self, number, digits): await self.update_peer_name() - # Wait a bit to allow some of the log lines to print before we prompt - await asyncio.sleep(1) - # Display a PIN code - print(color('###-----------------------------------', 'yellow')) - print(color(f'### Pairing with {self.peer_name}', 'yellow')) - print(color(f'### PIN: {number:0{digits}}', 'yellow')) - print(color('###-----------------------------------', 'yellow')) + self.print('###-----------------------------------') + self.print(f'### Pairing with {self.peer_name}') + self.print(f'### PIN: {number:0{digits}}') + self.print('###-----------------------------------') # ----------------------------------------------------------------------------- async def get_peer_name(peer, mode): if mode == 'classic': return await peer.request_name() - else: - # Try to get the peer name from GATT - services = await peer.discover_service(GATT_GENERIC_ACCESS_SERVICE) - if not services: - return None - values = await peer.read_characteristics_by_uuid(GATT_DEVICE_NAME_CHARACTERISTIC, services[0]) - if values: - return values[0].decode('utf-8') + # Try to get the peer name from GATT + services = await peer.discover_service(GATT_GENERIC_ACCESS_SERVICE) + if not services: + return None + + values = await peer.read_characteristics_by_uuid( + GATT_DEVICE_NAME_CHARACTERISTIC, services[0] + ) + if values: + return values[0].decode('utf-8') + + return None # ----------------------------------------------------------------------------- @@ -164,12 +187,12 @@ def read_with_error(connection): if AUTHENTICATION_ERROR_RETURNED[0]: return bytes([1]) - else: - AUTHENTICATION_ERROR_RETURNED[0] = True - raise ATT_Error(ATT_INSUFFICIENT_AUTHENTICATION_ERROR) + + AUTHENTICATION_ERROR_RETURNED[0] = True + raise ATT_Error(ATT_INSUFFICIENT_AUTHENTICATION_ERROR) -def write_with_error(connection, value): +def write_with_error(connection, _value): if not connection.is_encrypted: raise ATT_Error(ATT_INSUFFICIENT_ENCRYPTION_ERROR) @@ -183,14 +206,14 @@ def on_connection(connection, request): print(color(f'<<< Connection: {connection}', 'green')) # Listen for pairing events - connection.on('pairing_start', on_pairing_start) - connection.on('pairing', on_pairing) + connection.on('pairing_start', on_pairing_start) + connection.on('pairing', on_pairing) connection.on('pairing_failure', on_pairing_failure) # Listen for encryption changes connection.on( 'connection_encryption_change', - lambda: on_connection_encryption_change(connection) + lambda: on_connection_encryption_change(connection), ) # Request pairing if needed @@ -202,7 +225,12 @@ def on_connection(connection, request): # ----------------------------------------------------------------------------- def on_connection_encryption_change(connection): print(color('@@@-----------------------------------', 'blue')) - print(color(f'@@@ Connection is {"" if connection.is_encrypted else "not"}encrypted', 'blue')) + print( + color( + f'@@@ Connection is {"" if connection.is_encrypted else "not"}encrypted', + 'blue', + ) + ) print(color('@@@-----------------------------------', 'blue')) @@ -219,6 +247,7 @@ def on_pairing(keys): print(color('*** Paired!', 'cyan')) keys.print(prefix=color('*** ', 'cyan')) print(color('***-----------------------------------', 'cyan')) + Waiter.instance.terminate() # ----------------------------------------------------------------------------- @@ -226,6 +255,7 @@ def on_pairing_failure(reason): print(color('***-----------------------------------', 'red')) print(color(f'*** Pairing failed: {smp_error_name(reason)}', 'red')) print(color('***-----------------------------------', 'red')) + Waiter.instance.terminate() # ----------------------------------------------------------------------------- @@ -234,6 +264,7 @@ async def pair( sc, mitm, bond, + ctkd, io, prompt, request, @@ -241,8 +272,10 @@ async def pair( keystore_file, device_config, hci_transport, - address_or_name + address_or_name, ): + Waiter.instance = Waiter() + print('<<< connecting to HCI...') async with await open_transport_or_link(hci_transport) as (hci_source, hci_sink): print('<<< connected') @@ -270,11 +303,14 @@ async def pair( [ Characteristic( '552957FB-CF1F-4A31-9535-E78847E1A714', - Characteristic.READ | Characteristic.WRITE, + Characteristic.Properties.READ + | Characteristic.Properties.WRITE, Characteristic.READABLE | Characteristic.WRITEABLE, - CharacteristicValue(read=read_with_error, write=write_with_error) + CharacteristicValue( + read=read_with_error, write=write_with_error + ), ) - ] + ], ) ) @@ -282,16 +318,14 @@ async def pair( if mode == 'classic': device.classic_enabled = True device.le_enabled = False + device.classic_smp_enabled = ctkd # Get things going await device.power_on() # Set up a pairing config factory device.pairing_config_factory = lambda connection: PairingConfig( - sc, - mitm, - bond, - Delegate(mode, connection, io, prompt) + sc, mitm, bond, Delegate(mode, connection, io, prompt) ) # Connect to a peer or wait for a connection @@ -311,29 +345,114 @@ async def pair( print(color(f'Pairing failed: {error}', 'red')) return else: - # Advertise so that peers can find us and connect - await device.start_advertising(auto_restart=True) + if mode == 'le': + # Advertise so that peers can find us and connect + await device.start_advertising(auto_restart=True) + else: + # Become discoverable and connectable + await device.set_discoverable(True) + await device.set_connectable(True) + + # Run until the user asks to exit + await Waiter.instance.wait_until_terminated() + + +# ----------------------------------------------------------------------------- +class LogHandler(logging.Handler): + def __init__(self): + super().__init__() + self.setFormatter(logging.Formatter('%(levelname)s:%(name)s:%(message)s')) - await hci_source.wait_for_termination() + def emit(self, record): + message = self.format(record) + print(message) # ----------------------------------------------------------------------------- @click.command() -@click.option('--mode', type=click.Choice(['le', 'classic']), default='le', show_default=True) -@click.option('--sc', type=bool, default=True, help='Use the Secure Connections protocol', show_default=True) -@click.option('--mitm', type=bool, default=True, help='Request MITM protection', show_default=True) -@click.option('--bond', type=bool, default=True, help='Enable bonding', show_default=True) -@click.option('--io', type=click.Choice(['keyboard', 'display', 'display+keyboard', 'display+yes/no', 'none']), default='display+keyboard', show_default=True) +@click.option( + '--mode', type=click.Choice(['le', 'classic']), default='le', show_default=True +) +@click.option( + '--sc', + type=bool, + default=True, + help='Use the Secure Connections protocol', + show_default=True, +) +@click.option( + '--mitm', type=bool, default=True, help='Request MITM protection', show_default=True +) +@click.option( + '--bond', type=bool, default=True, help='Enable bonding', show_default=True +) +@click.option( + '--ctkd', + type=bool, + default=True, + help='Enable CTKD', + show_default=True, +) +@click.option( + '--io', + type=click.Choice( + ['keyboard', 'display', 'display+keyboard', 'display+yes/no', 'none'] + ), + default='display+keyboard', + show_default=True, +) @click.option('--prompt', is_flag=True, help='Prompt to accept/reject pairing request') -@click.option('--request', is_flag=True, help='Request that the connecting peer initiate pairing') +@click.option( + '--request', is_flag=True, help='Request that the connecting peer initiate pairing' +) @click.option('--print-keys', is_flag=True, help='Print the bond keys before pairing') -@click.option('--keystore-file', help='File in which to store the pairing keys') +@click.option( + '--keystore-file', + metavar='<filename>', + help='File in which to store the pairing keys', +) @click.argument('device-config') @click.argument('hci_transport') @click.argument('address-or-name', required=False) -def main(mode, sc, mitm, bond, io, prompt, request, print_keys, keystore_file, device_config, hci_transport, address_or_name): - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) - asyncio.run(pair(mode, sc, mitm, bond, io, prompt, request, print_keys, keystore_file, device_config, hci_transport, address_or_name)) +def main( + mode, + sc, + mitm, + bond, + ctkd, + io, + prompt, + request, + print_keys, + keystore_file, + device_config, + hci_transport, + address_or_name, +): + # Setup logging + log_handler = LogHandler() + root_logger = logging.getLogger() + root_logger.addHandler(log_handler) + root_logger.setLevel(os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + + # Pair + asyncio.run( + pair( + mode, + sc, + mitm, + bond, + ctkd, + io, + prompt, + request, + print_keys, + keystore_file, + device_config, + hci_transport, + address_or_name, + ) + ) # ----------------------------------------------------------------------------- diff --git a/apps/pandora_server.py b/apps/pandora_server.py new file mode 100644 index 0000000..5f92309 --- /dev/null +++ b/apps/pandora_server.py @@ -0,0 +1,30 @@ +import asyncio +import click +import logging + +from bumble.pandora import PandoraDevice, serve + +BUMBLE_SERVER_GRPC_PORT = 7999 +ROOTCANAL_PORT_CUTTLEFISH = 7300 + + +@click.command() +@click.option('--grpc-port', help='gRPC port to serve', default=BUMBLE_SERVER_GRPC_PORT) +@click.option( + '--rootcanal-port', help='Rootcanal TCP port', default=ROOTCANAL_PORT_CUTTLEFISH +) +@click.option( + '--transport', + help='HCI transport', + default=f'tcp-client:127.0.0.1:<rootcanal-port>', +) +def main(grpc_port: int, rootcanal_port: int, transport: str) -> None: + if '<rootcanal-port>' in transport: + transport = transport.replace('<rootcanal-port>', str(rootcanal_port)) + device = PandoraDevice({'transport': transport}) + logging.basicConfig(level=logging.DEBUG) + asyncio.run(serve(device, port=grpc_port)) + + +if __name__ == '__main__': + main() # pylint: disable=no-value-for-parameter diff --git a/apps/scan.py b/apps/scan.py index 045cb57..dac7a2c 100644 --- a/apps/scan.py +++ b/apps/scan.py @@ -19,20 +19,20 @@ import asyncio import os import logging import click -from colors import color +from bumble.colors import color from bumble.device import Device from bumble.transport import open_transport_or_link from bumble.keys import JsonKeyStore from bumble.smp import AddressResolver -from bumble.hci import HCI_LE_Advertising_Report_Event -from bumble.core import AdvertisingData +from bumble.device import Advertisement +from bumble.hci import HCI_Constant, HCI_LE_1M_PHY, HCI_LE_CODED_PHY # ----------------------------------------------------------------------------- def make_rssi_bar(rssi): - DISPLAY_MIN_RSSI = -105 - DISPLAY_MAX_RSSI = -30 + DISPLAY_MIN_RSSI = -105 + DISPLAY_MAX_RSSI = -30 DEFAULT_RSSI_BAR_WIDTH = 30 blocks = ['', 'â–', 'â–Ž', 'â–', 'â–Œ', 'â–‹', 'â–Š', 'â–‰'] @@ -48,19 +48,24 @@ class AdvertisementPrinter: self.min_rssi = min_rssi self.resolver = resolver - def print_advertisement(self, address, address_color, ad_data, rssi): - if self.min_rssi is not None and rssi < self.min_rssi: + def print_advertisement(self, advertisement): + address = advertisement.address + address_color = 'yellow' if advertisement.is_connectable else 'red' + + if self.min_rssi is not None and advertisement.rssi < self.min_rssi: return address_qualifier = '' resolution_qualifier = '' - if self.resolver and address.is_resolvable: - resolved = self.resolver.resolve(address) + if self.resolver and advertisement.address.is_resolvable: + resolved = self.resolver.resolve(advertisement.address) if resolved is not None: - resolution_qualifier = f'(resolved from {address})' + resolution_qualifier = f'(resolved from {advertisement.address})' address = resolved - address_type_string = ('PUBLIC', 'RANDOM', 'PUBLIC_ID', 'RANDOM_ID')[address.address_type] + address_type_string = ('PUBLIC', 'RANDOM', 'PUBLIC_ID', 'RANDOM_ID')[ + address.address_type + ] if address.is_public: type_color = 'cyan' else: @@ -74,18 +79,32 @@ class AdvertisementPrinter: type_color = 'blue' address_qualifier = '(non-resolvable)' - rssi_bar = make_rssi_bar(rssi) separator = '\n ' - print(f'>>> {color(address, address_color)} [{color(address_type_string, type_color)}]{address_qualifier}{resolution_qualifier}:{separator}RSSI:{rssi:4} {rssi_bar}{separator}{ad_data.to_string(separator)}\n') + rssi_bar = make_rssi_bar(advertisement.rssi) + if not advertisement.is_legacy: + phy_info = ( + f'PHY: {HCI_Constant.le_phy_name(advertisement.primary_phy)}/' + f'{HCI_Constant.le_phy_name(advertisement.secondary_phy)} ' + f'{separator}' + ) + else: + phy_info = '' + + print( + f'>>> {color(address, address_color)} ' + f'[{color(address_type_string, type_color)}]{address_qualifier}' + f'{resolution_qualifier}:{separator}' + f'{phy_info}' + f'RSSI:{advertisement.rssi:4} {rssi_bar}{separator}' + f'{advertisement.data.to_string(separator)}\n' + ) - def on_advertisement(self, address, ad_data, rssi, connectable): - address_color = 'yellow' if connectable else 'red' - self.print_advertisement(address, address_color, ad_data, rssi) + def on_advertisement(self, advertisement): + self.print_advertisement(advertisement) - def on_advertising_report(self, address, ad_data, rssi, event_type): - print(f'{color("EVENT", "green")}: {HCI_LE_Advertising_Report_Event.event_type_name(event_type)}') - ad_data = AdvertisingData.from_bytes(ad_data) - self.print_advertisement(address, 'yellow', ad_data, rssi) + def on_advertising_report(self, report): + print(f'{color("EVENT", "green")}: {report.event_type_string()}') + self.print_advertisement(Advertisement.from_advertising_report(report)) # ----------------------------------------------------------------------------- @@ -94,20 +113,25 @@ async def scan( passive, scan_interval, scan_window, + phy, filter_duplicates, raw, keystore_file, device_config, - transport + transport, ): print('<<< connecting to HCI...') async with await open_transport_or_link(transport) as (hci_source, hci_sink): print('<<< connected') if device_config: - device = Device.from_config_file_with_hci(device_config, hci_source, hci_sink) + device = Device.from_config_file_with_hci( + device_config, hci_source, hci_sink + ) else: - device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink) + device = Device.with_hci( + 'Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink + ) if keystore_file: keystore = JsonKeyStore(namespace=None, filename=keystore_file) @@ -126,11 +150,18 @@ async def scan( device.on('advertisement', printer.on_advertisement) await device.power_on() + + if phy is None: + scanning_phys = [HCI_LE_1M_PHY, HCI_LE_CODED_PHY] + else: + scanning_phys = [{'1m': HCI_LE_1M_PHY, 'coded': HCI_LE_CODED_PHY}[phy]] + await device.start_scanning( active=(not passive), scan_interval=scan_interval, scan_window=scan_window, - filter_duplicates=filter_duplicates + filter_duplicates=filter_duplicates, + scanning_phys=scanning_phys, ) await hci_source.wait_for_termination() @@ -142,14 +173,51 @@ async def scan( @click.option('--passive', is_flag=True, default=False, help='Perform passive scanning') @click.option('--scan-interval', type=int, default=60, help='Scan interval') @click.option('--scan-window', type=int, default=60, help='Scan window') -@click.option('--filter-duplicates', type=bool, default=True, help='Filter duplicates at the controller level') -@click.option('--raw', is_flag=True, default=False, help='Listen for raw advertising reports instead of processed ones') +@click.option( + '--phy', type=click.Choice(['1m', 'coded']), help='Only scan on the specified PHY' +) +@click.option( + '--filter-duplicates', + type=bool, + default=True, + help='Filter duplicates at the controller level', +) +@click.option( + '--raw', + is_flag=True, + default=False, + help='Listen for raw advertising reports instead of processed ones', +) @click.option('--keystore-file', help='Keystore file to use when resolving addresses') @click.option('--device-config', help='Device config file for the scanning device') @click.argument('transport') -def main(min_rssi, passive, scan_interval, scan_window, filter_duplicates, raw, keystore_file, device_config, transport): - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper()) - asyncio.run(scan(min_rssi, passive, scan_interval, scan_window, filter_duplicates, raw, keystore_file, device_config, transport)) +def main( + min_rssi, + passive, + scan_interval, + scan_window, + phy, + filter_duplicates, + raw, + keystore_file, + device_config, + transport, +): + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper()) + asyncio.run( + scan( + min_rssi, + passive, + scan_interval, + scan_window, + phy, + filter_duplicates, + raw, + keystore_file, + device_config, + transport, + ) + ) # ----------------------------------------------------------------------------- diff --git a/apps/show.py b/apps/show.py index bba6c66..bf01ead 100644 --- a/apps/show.py +++ b/apps/show.py @@ -17,8 +17,8 @@ # ----------------------------------------------------------------------------- import struct import click -from colors import color +from bumble.colors import color from bumble import hci from bumble.transport.common import PacketReader from bumble.helpers import PacketTracer @@ -27,13 +27,14 @@ from bumble.helpers import PacketTracer # ----------------------------------------------------------------------------- class SnoopPacketReader: ''' - Reader that reads HCI packets from a "snoop" file (based on RFC 1761, but not exactly the same...) + Reader that reads HCI packets from a "snoop" file (based on RFC 1761, but not + exactly the same...) ''' - DATALINK_H1 = 1001 - DATALINK_H4 = 1002 + DATALINK_H1 = 1001 + DATALINK_H4 = 1002 DATALINK_BSCP = 1003 - DATALINK_H5 = 1004 + DATALINK_H5 = 1004 def __init__(self, source): self.source = source @@ -41,9 +42,13 @@ class SnoopPacketReader: # Read the header identification_pattern = source.read(8) if identification_pattern.hex().lower() != '6274736e6f6f7000': - raise ValueError('not a valid snoop file, unexpected identification pattern') - (self.version_number, self.data_link_type) = struct.unpack('>II', source.read(8)) - if self.data_link_type != self.DATALINK_H4 and self.data_link_type != self.DATALINK_H1: + raise ValueError( + 'not a valid snoop file, unexpected identification pattern' + ) + (self.version_number, self.data_link_type) = struct.unpack( + '>II', source.read(8) + ) + if self.data_link_type not in (self.DATALINK_H4, self.DATALINK_H1): raise ValueError(f'datalink type {self.data_link_type} not supported') def next_packet(self): @@ -55,9 +60,9 @@ class SnoopPacketReader: original_length, included_length, packet_flags, - cumulative_drops, - timestamp_seconds, - timestamp_microsecond + _cumulative_drops, + _timestamp_seconds, + _timestamp_microsecond, ) = struct.unpack('>IIIIII', header) # Abort on truncated packets @@ -79,24 +84,34 @@ class SnoopPacketReader: else: packet_type = hci.HCI_ACL_DATA_PACKET - return (packet_flags & 1, bytes([packet_type]) + self.source.read(included_length)) - else: - return (packet_flags & 1, self.source.read(included_length)) + return ( + packet_flags & 1, + bytes([packet_type]) + self.source.read(included_length), + ) + + return (packet_flags & 1, self.source.read(included_length)) # ----------------------------------------------------------------------------- # Main # ----------------------------------------------------------------------------- @click.command() -@click.option('--format', type=click.Choice(['h4', 'snoop']), default='h4', help='Format of the input file') +@click.option( + '--format', + type=click.Choice(['h4', 'snoop']), + default='h4', + help='Format of the input file', +) @click.argument('filename') -def show(format, filename): +# pylint: disable=redefined-builtin +def main(format, filename): input = open(filename, 'rb') if format == 'h4': packet_reader = PacketReader(input) def read_next_packet(): - (0, packet_reader.next_packet()) + return (0, packet_reader.next_packet()) + else: packet_reader = SnoopPacketReader(input) read_next_packet = packet_reader.next_packet @@ -112,9 +127,8 @@ def show(format, filename): except Exception as error: print(color(f'!!! {error}', 'red')) - pass # ----------------------------------------------------------------------------- if __name__ == '__main__': - show() + main() # pylint: disable=no-value-for-parameter diff --git a/apps/unbond.py b/apps/unbond.py index cf1877c..105d9a4 100644 --- a/apps/unbond.py +++ b/apps/unbond.py @@ -54,7 +54,7 @@ async def unbond(keystore_file, device_config, address): @click.argument('device-config') @click.argument('address', required=False) def main(keystore_file, device_config, address): - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) asyncio.run(unbond(keystore_file, device_config, address)) diff --git a/apps/usb_probe.py b/apps/usb_probe.py new file mode 100644 index 0000000..785b0dd --- /dev/null +++ b/apps/usb_probe.py @@ -0,0 +1,278 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# This tool lists all the USB devices, with details about each device. +# For each device, the different possible Bumble transport strings that can +# refer to it are listed. If the device is known to be a Bluetooth HCI device, +# its identifier is printed in reverse colors, and the transport names in cyan color. +# For other devices, regardless of their type, the transport names are printed +# in red. Whether that device is actually a Bluetooth device or not depends on +# whether it is a Bluetooth device that uses a non-standard Class, or some other +# type of device (there's no way to tell). +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import os +import logging +import click +import usb1 + +from bumble.colors import color +from bumble.transport.usb import load_libusb + + +# ----------------------------------------------------------------------------- +# Constants +# ----------------------------------------------------------------------------- +USB_DEVICE_CLASS_DEVICE = 0x00 +USB_DEVICE_CLASS_WIRELESS_CONTROLLER = 0xE0 +USB_DEVICE_SUBCLASS_RF_CONTROLLER = 0x01 +USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER = 0x01 + +USB_DEVICE_CLASSES = { + 0x00: 'Device', + 0x01: 'Audio', + 0x02: 'Communications and CDC Control', + 0x03: 'Human Interface Device', + 0x05: 'Physical', + 0x06: 'Still Imaging', + 0x07: 'Printer', + 0x08: 'Mass Storage', + 0x09: 'Hub', + 0x0A: 'CDC Data', + 0x0B: 'Smart Card', + 0x0D: 'Content Security', + 0x0E: 'Video', + 0x0F: 'Personal Healthcare', + 0x10: 'Audio/Video', + 0x11: 'Billboard', + 0x12: 'USB Type-C Bridge', + 0x3C: 'I3C', + 0xDC: 'Diagnostic', + USB_DEVICE_CLASS_WIRELESS_CONTROLLER: ( + 'Wireless Controller', + { + 0x01: { + 0x01: 'Bluetooth', + 0x02: 'UWB', + 0x03: 'Remote NDIS', + 0x04: 'Bluetooth AMP', + } + }, + ), + 0xEF: 'Miscellaneous', + 0xFE: 'Application Specific', + 0xFF: 'Vendor Specific', +} + +USB_ENDPOINT_IN = 0x80 +USB_ENDPOINT_TYPES = ['CONTROL', 'ISOCHRONOUS', 'BULK', 'INTERRUPT'] + +USB_BT_HCI_CLASS_TUPLE = ( + USB_DEVICE_CLASS_WIRELESS_CONTROLLER, + USB_DEVICE_SUBCLASS_RF_CONTROLLER, + USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER, +) + + +# ----------------------------------------------------------------------------- +def show_device_details(device): + for configuration in device: + print(f' Configuration {configuration.getConfigurationValue()}') + for interface in configuration: + for setting in interface: + alternate_setting = setting.getAlternateSetting() + suffix = ( + f'/{alternate_setting}' if interface.getNumSettings() > 1 else '' + ) + (class_string, subclass_string) = get_class_info( + setting.getClass(), setting.getSubClass(), setting.getProtocol() + ) + details = f'({class_string}, {subclass_string})' + print(f' Interface: {setting.getNumber()}{suffix} {details}') + for endpoint in setting: + endpoint_type = USB_ENDPOINT_TYPES[endpoint.getAttributes() & 3] + endpoint_direction = ( + 'OUT' + if (endpoint.getAddress() & USB_ENDPOINT_IN == 0) + else 'IN' + ) + print( + f' Endpoint 0x{endpoint.getAddress():02X}: ' + f'{endpoint_type} {endpoint_direction}' + ) + + +# ----------------------------------------------------------------------------- +def get_class_info(cls, subclass, protocol): + class_info = USB_DEVICE_CLASSES.get(cls) + protocol_string = '' + if class_info is None: + class_string = f'0x{cls:02X}' + else: + if isinstance(class_info, tuple): + class_string = class_info[0] + subclass_info = class_info[1].get(subclass) + if subclass_info: + protocol_string = subclass_info.get(protocol) + if protocol_string is not None: + protocol_string = f' [{protocol_string}]' + + else: + class_string = class_info + + subclass_string = f'{subclass}/{protocol}{protocol_string}' + + return (class_string, subclass_string) + + +# ----------------------------------------------------------------------------- +def is_bluetooth_hci(device): + # Check if the device class indicates a match + if ( + device.getDeviceClass(), + device.getDeviceSubClass(), + device.getDeviceProtocol(), + ) == USB_BT_HCI_CLASS_TUPLE: + return True + + # If the device class is 'Device', look for a matching interface + if device.getDeviceClass() == USB_DEVICE_CLASS_DEVICE: + for configuration in device: + for interface in configuration: + for setting in interface: + if ( + setting.getClass(), + setting.getSubClass(), + setting.getProtocol(), + ) == USB_BT_HCI_CLASS_TUPLE: + return True + + return False + + +# ----------------------------------------------------------------------------- +@click.command() +@click.option('--verbose', is_flag=True, default=False, help='Print more details') +def main(verbose): + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'WARNING').upper()) + + load_libusb() + with usb1.USBContext() as context: + bluetooth_device_count = 0 + devices = {} + + for device in context.getDeviceIterator(skip_on_error=True): + device_class = device.getDeviceClass() + device_subclass = device.getDeviceSubClass() + device_protocol = device.getDeviceProtocol() + + device_id = (device.getVendorID(), device.getProductID()) + + (device_class_string, device_subclass_string) = get_class_info( + device_class, device_subclass, device_protocol + ) + + try: + device_serial_number = device.getSerialNumber() + except usb1.USBError: + device_serial_number = None + + try: + device_manufacturer = device.getManufacturer() + except usb1.USBError: + device_manufacturer = None + + try: + device_product = device.getProduct() + except usb1.USBError: + device_product = None + + device_is_bluetooth_hci = is_bluetooth_hci(device) + if device_is_bluetooth_hci: + bluetooth_device_count += 1 + fg_color = 'black' + bg_color = 'yellow' + else: + fg_color = 'yellow' + bg_color = 'black' + + # Compute the different ways this can be referenced as a Bumble transport + bumble_transport_names = [] + basic_transport_name = ( + f'usb:{device.getVendorID():04X}:{device.getProductID():04X}' + ) + + if device_is_bluetooth_hci: + bumble_transport_names.append(f'usb:{bluetooth_device_count - 1}') + + if device_id not in devices: + bumble_transport_names.append(basic_transport_name) + else: + bumble_transport_names.append( + f'{basic_transport_name}#{len(devices[device_id])}' + ) + + if device_serial_number is not None: + if ( + device_id not in devices + or device_serial_number not in devices[device_id] + ): + bumble_transport_names.append( + f'{basic_transport_name}/{device_serial_number}' + ) + + # Print the results + print( + color( + f'ID {device.getVendorID():04X}:{device.getProductID():04X}', + fg=fg_color, + bg=bg_color, + ) + ) + if bumble_transport_names: + print( + color(' Bumble Transport Names:', 'blue'), + ' or '.join( + color(x, 'cyan' if device_is_bluetooth_hci else 'red') + for x in bumble_transport_names + ), + ) + print( + color(' Bus/Device: ', 'green'), + f'{device.getBusNumber():03}/{device.getDeviceAddress():03}', + ) + print(color(' Class: ', 'green'), device_class_string) + print(color(' Subclass/Protocol: ', 'green'), device_subclass_string) + if device_serial_number is not None: + print(color(' Serial: ', 'green'), device_serial_number) + if device_manufacturer is not None: + print(color(' Manufacturer: ', 'green'), device_manufacturer) + if device_product is not None: + print(color(' Product: ', 'green'), device_product) + + if verbose: + show_device_details(device) + + print() + + devices.setdefault(device_id, []).append(device_serial_number) + + +# ----------------------------------------------------------------------------- +if __name__ == '__main__': + main() # pylint: disable=no-value-for-parameter diff --git a/bumble/__init__.py b/bumble/__init__.py index e69de29..8a067ca 100644 --- a/bumble/__init__.py +++ b/bumble/__init__.py @@ -0,0 +1,4 @@ +try: + from ._version import version as __version__ +except ImportError: + __version__ = "unknown version" diff --git a/bumble/a2dp.py b/bumble/a2dp.py index 03c3fd2..772846a 100644 --- a/bumble/a2dp.py +++ b/bumble/a2dp.py @@ -16,10 +16,8 @@ # Imports # ----------------------------------------------------------------------------- import struct -import bitstruct import logging from collections import namedtuple -from colors import color from .company_ids import COMPANY_IDENTIFIERS from .sdp import ( @@ -30,7 +28,7 @@ from .sdp import ( SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, - SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, ) from .core import ( BT_L2CAP_PROTOCOL_ID, @@ -38,7 +36,7 @@ from .core import ( BT_AUDIO_SINK_SERVICE, BT_AVDTP_PROTOCOL_ID, BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE, - name_or_number + name_or_number, ) @@ -51,6 +49,7 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off A2DP_SBC_CODEC_TYPE = 0x00 A2DP_MPEG_1_2_AUDIO_CODEC_TYPE = 0x01 @@ -127,71 +126,115 @@ MPEG_2_4_OBJECT_TYPE_NAMES = { MPEG_4_AAC_SCALABLE_OBJECT_TYPE: 'MPEG_4_AAC_SCALABLE_OBJECT_TYPE' } +# fmt: on + # ----------------------------------------------------------------------------- def flags_to_list(flags, values): result = [] - for i in range(len(values)): + for i, value in enumerate(values): if flags & (1 << (len(values) - i - 1)): - result.append(values[i]) + result.append(value) return result # ----------------------------------------------------------------------------- def make_audio_source_service_sdp_records(service_record_handle, version=(1, 3)): + # pylint: disable=import-outside-toplevel from .avdtp import AVDTP_PSM + version_int = version[0] << 8 | version[1] return [ - ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(service_record_handle)), - ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT) - ])), - ServiceAttribute(SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.uuid(BT_AUDIO_SOURCE_SERVICE) - ])), - ServiceAttribute(SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.sequence([ - DataElement.uuid(BT_L2CAP_PROTOCOL_ID), - DataElement.unsigned_integer_16(AVDTP_PSM) - ]), - DataElement.sequence([ - DataElement.uuid(BT_AVDTP_PROTOCOL_ID), - DataElement.unsigned_integer_16(version_int) - ]) - ])), - ServiceAttribute(SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE), - DataElement.unsigned_integer_16(version_int) - ])), + ServiceAttribute( + SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, + DataElement.unsigned_integer_32(service_record_handle), + ), + ServiceAttribute( + SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, + DataElement.sequence([DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)]), + ), + ServiceAttribute( + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + DataElement.sequence([DataElement.uuid(BT_AUDIO_SOURCE_SERVICE)]), + ), + ServiceAttribute( + SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [ + DataElement.sequence( + [ + DataElement.uuid(BT_L2CAP_PROTOCOL_ID), + DataElement.unsigned_integer_16(AVDTP_PSM), + ] + ), + DataElement.sequence( + [ + DataElement.uuid(BT_AVDTP_PROTOCOL_ID), + DataElement.unsigned_integer_16(version_int), + ] + ), + ] + ), + ), + ServiceAttribute( + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [ + DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE), + DataElement.unsigned_integer_16(version_int), + ] + ), + ), ] # ----------------------------------------------------------------------------- def make_audio_sink_service_sdp_records(service_record_handle, version=(1, 3)): + # pylint: disable=import-outside-toplevel from .avdtp import AVDTP_PSM + version_int = version[0] << 8 | version[1] return [ - ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(service_record_handle)), - ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT) - ])), - ServiceAttribute(SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.uuid(BT_AUDIO_SINK_SERVICE) - ])), - ServiceAttribute(SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.sequence([ - DataElement.uuid(BT_L2CAP_PROTOCOL_ID), - DataElement.unsigned_integer_16(AVDTP_PSM) - ]), - DataElement.sequence([ - DataElement.uuid(BT_AVDTP_PROTOCOL_ID), - DataElement.unsigned_integer_16(version_int) - ]) - ])), - ServiceAttribute(SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE), - DataElement.unsigned_integer_16(version_int) - ])), + ServiceAttribute( + SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, + DataElement.unsigned_integer_32(service_record_handle), + ), + ServiceAttribute( + SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, + DataElement.sequence([DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)]), + ), + ServiceAttribute( + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + DataElement.sequence([DataElement.uuid(BT_AUDIO_SINK_SERVICE)]), + ), + ServiceAttribute( + SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [ + DataElement.sequence( + [ + DataElement.uuid(BT_L2CAP_PROTOCOL_ID), + DataElement.unsigned_integer_16(AVDTP_PSM), + ] + ), + DataElement.sequence( + [ + DataElement.uuid(BT_AVDTP_PROTOCOL_ID), + DataElement.unsigned_integer_16(version_int), + ] + ), + ] + ), + ), + ServiceAttribute( + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [ + DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE), + DataElement.unsigned_integer_16(version_int), + ] + ), + ), ] @@ -206,45 +249,46 @@ class SbcMediaCodecInformation( 'subbands', 'allocation_method', 'minimum_bitpool_value', - 'maximum_bitpool_value' - ] + 'maximum_bitpool_value', + ], ) ): ''' A2DP spec - 4.3.2 Codec Specific Information Elements ''' - BIT_FIELDS = 'u4u4u4u2u2u8u8' - SAMPLING_FREQUENCY_BITS = { - 16000: 1 << 3, - 32000: 1 << 2, - 44100: 1 << 1, - 48000: 1 - } + SAMPLING_FREQUENCY_BITS = {16000: 1 << 3, 32000: 1 << 2, 44100: 1 << 1, 48000: 1} CHANNEL_MODE_BITS = { - SBC_MONO_CHANNEL_MODE: 1 << 3, - SBC_DUAL_CHANNEL_MODE: 1 << 2, - SBC_STEREO_CHANNEL_MODE: 1 << 1, - SBC_JOINT_STEREO_CHANNEL_MODE: 1 - } - BLOCK_LENGTH_BITS = { - 4: 1 << 3, - 8: 1 << 2, - 12: 1 << 1, - 16: 1 - } - SUBBANDS_BITS = { - 4: 1 << 1, - 8: 1 + SBC_MONO_CHANNEL_MODE: 1 << 3, + SBC_DUAL_CHANNEL_MODE: 1 << 2, + SBC_STEREO_CHANNEL_MODE: 1 << 1, + SBC_JOINT_STEREO_CHANNEL_MODE: 1, } + BLOCK_LENGTH_BITS = {4: 1 << 3, 8: 1 << 2, 12: 1 << 1, 16: 1} + SUBBANDS_BITS = {4: 1 << 1, 8: 1} ALLOCATION_METHOD_BITS = { - SBC_SNR_ALLOCATION_METHOD: 1 << 1, - SBC_LOUDNESS_ALLOCATION_METHOD: 1 + SBC_SNR_ALLOCATION_METHOD: 1 << 1, + SBC_LOUDNESS_ALLOCATION_METHOD: 1, } @staticmethod - def from_bytes(data): - return SbcMediaCodecInformation(*bitstruct.unpack(SbcMediaCodecInformation.BIT_FIELDS, data)) + def from_bytes(data: bytes) -> 'SbcMediaCodecInformation': + sampling_frequency = (data[0] >> 4) & 0x0F + channel_mode = (data[0] >> 0) & 0x0F + block_length = (data[1] >> 4) & 0x0F + subbands = (data[1] >> 2) & 0x03 + allocation_method = (data[1] >> 0) & 0x03 + minimum_bitpool_value = (data[2] >> 0) & 0xFF + maximum_bitpool_value = (data[3] >> 0) & 0xFF + return SbcMediaCodecInformation( + sampling_frequency, + channel_mode, + block_length, + subbands, + allocation_method, + minimum_bitpool_value, + maximum_bitpool_value, + ) @classmethod def from_discrete_values( @@ -255,16 +299,16 @@ class SbcMediaCodecInformation( subbands, allocation_method, minimum_bitpool_value, - maximum_bitpool_value + maximum_bitpool_value, ): return SbcMediaCodecInformation( - sampling_frequency = cls.SAMPLING_FREQUENCY_BITS[sampling_frequency], - channel_mode = cls.CHANNEL_MODE_BITS[channel_mode], - block_length = cls.BLOCK_LENGTH_BITS[block_length], - subbands = cls.SUBBANDS_BITS[subbands], - allocation_method = cls.ALLOCATION_METHOD_BITS[allocation_method], - minimum_bitpool_value = minimum_bitpool_value, - maximum_bitpool_value = maximum_bitpool_value + sampling_frequency=cls.SAMPLING_FREQUENCY_BITS[sampling_frequency], + channel_mode=cls.CHANNEL_MODE_BITS[channel_mode], + block_length=cls.BLOCK_LENGTH_BITS[block_length], + subbands=cls.SUBBANDS_BITS[subbands], + allocation_method=cls.ALLOCATION_METHOD_BITS[allocation_method], + minimum_bitpool_value=minimum_bitpool_value, + maximum_bitpool_value=maximum_bitpool_value, ) @classmethod @@ -276,63 +320,71 @@ class SbcMediaCodecInformation( subbands, allocation_methods, minimum_bitpool_value, - maximum_bitpool_value + maximum_bitpool_value, ): return SbcMediaCodecInformation( - sampling_frequency = sum(cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies), - channel_mode = sum(cls.CHANNEL_MODE_BITS[x] for x in channel_modes), - block_length = sum(cls.BLOCK_LENGTH_BITS[x] for x in block_lengths), - subbands = sum(cls.SUBBANDS_BITS[x] for x in subbands), - allocation_method = sum(cls.ALLOCATION_METHOD_BITS[x] for x in allocation_methods), - minimum_bitpool_value = minimum_bitpool_value, - maximum_bitpool_value = maximum_bitpool_value + sampling_frequency=sum( + cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies + ), + channel_mode=sum(cls.CHANNEL_MODE_BITS[x] for x in channel_modes), + block_length=sum(cls.BLOCK_LENGTH_BITS[x] for x in block_lengths), + subbands=sum(cls.SUBBANDS_BITS[x] for x in subbands), + allocation_method=sum( + cls.ALLOCATION_METHOD_BITS[x] for x in allocation_methods + ), + minimum_bitpool_value=minimum_bitpool_value, + maximum_bitpool_value=maximum_bitpool_value, ) - def __bytes__(self): - return bitstruct.pack(self.BIT_FIELDS, *self) + def __bytes__(self) -> bytes: + return bytes( + [ + (self.sampling_frequency << 4) | self.channel_mode, + (self.block_length << 4) + | (self.subbands << 2) + | self.allocation_method, + self.minimum_bitpool_value, + self.maximum_bitpool_value, + ] + ) def __str__(self): channel_modes = ['MONO', 'DUAL_CHANNEL', 'STEREO', 'JOINT_STEREO'] allocation_methods = ['SNR', 'Loudness'] - return '\n'.join([ - 'SbcMediaCodecInformation(', - f' sampling_frequency: {",".join([str(x) for x in flags_to_list(self.sampling_frequency, SBC_SAMPLING_FREQUENCIES)])}', - f' channel_mode: {",".join([str(x) for x in flags_to_list(self.channel_mode, channel_modes)])}', - f' block_length: {",".join([str(x) for x in flags_to_list(self.block_length, SBC_BLOCK_LENGTHS)])}', - f' subbands: {",".join([str(x) for x in flags_to_list(self.subbands, SBC_SUBBANDS)])}', - f' allocation_method: {",".join([str(x) for x in flags_to_list(self.allocation_method, allocation_methods)])}', - f' minimum_bitpool_value: {self.minimum_bitpool_value}', - f' maximum_bitpool_value: {self.maximum_bitpool_value}' - ')' - ]) + return '\n'.join( + # pylint: disable=line-too-long + [ + 'SbcMediaCodecInformation(', + f' sampling_frequency: {",".join([str(x) for x in flags_to_list(self.sampling_frequency, SBC_SAMPLING_FREQUENCIES)])}', + f' channel_mode: {",".join([str(x) for x in flags_to_list(self.channel_mode, channel_modes)])}', + f' block_length: {",".join([str(x) for x in flags_to_list(self.block_length, SBC_BLOCK_LENGTHS)])}', + f' subbands: {",".join([str(x) for x in flags_to_list(self.subbands, SBC_SUBBANDS)])}', + f' allocation_method: {",".join([str(x) for x in flags_to_list(self.allocation_method, allocation_methods)])}', + f' minimum_bitpool_value: {self.minimum_bitpool_value}', + f' maximum_bitpool_value: {self.maximum_bitpool_value}' ')', + ] + ) # ----------------------------------------------------------------------------- class AacMediaCodecInformation( namedtuple( 'AacMediaCodecInformation', - [ - 'object_type', - 'sampling_frequency', - 'channels', - 'vbr', - 'bitrate' - ] + ['object_type', 'sampling_frequency', 'channels', 'rfa', 'vbr', 'bitrate'], ) ): ''' A2DP spec - 4.5.2 Codec Specific Information Elements ''' - BIT_FIELDS = 'u8u12u2p2u1u23' OBJECT_TYPE_BITS = { - MPEG_2_AAC_LC_OBJECT_TYPE: 1 << 7, - MPEG_4_AAC_LC_OBJECT_TYPE: 1 << 6, - MPEG_4_AAC_LTP_OBJECT_TYPE: 1 << 5, - MPEG_4_AAC_SCALABLE_OBJECT_TYPE: 1 << 4 + MPEG_2_AAC_LC_OBJECT_TYPE: 1 << 7, + MPEG_4_AAC_LC_OBJECT_TYPE: 1 << 6, + MPEG_4_AAC_LTP_OBJECT_TYPE: 1 << 5, + MPEG_4_AAC_SCALABLE_OBJECT_TYPE: 1 << 4, } SAMPLING_FREQUENCY_BITS = { - 8000: 1 << 11, + 8000: 1 << 11, 11025: 1 << 10, 12000: 1 << 9, 16000: 1 << 8, @@ -343,66 +395,82 @@ class AacMediaCodecInformation( 48000: 1 << 3, 64000: 1 << 2, 88200: 1 << 1, - 96000: 1 - } - CHANNELS_BITS = { - 1: 1 << 1, - 2: 1 + 96000: 1, } + CHANNELS_BITS = {1: 1 << 1, 2: 1} @staticmethod - def from_bytes(data): - return AacMediaCodecInformation(*bitstruct.unpack(AacMediaCodecInformation.BIT_FIELDS, data)) + def from_bytes(data: bytes) -> 'AacMediaCodecInformation': + object_type = data[0] + sampling_frequency = (data[1] << 4) | ((data[2] >> 4) & 0x0F) + channels = (data[2] >> 2) & 0x03 + rfa = 0 + vbr = (data[3] >> 7) & 0x01 + bitrate = ((data[3] & 0x7F) << 16) | (data[4] << 8) | data[5] + return AacMediaCodecInformation( + object_type, sampling_frequency, channels, rfa, vbr, bitrate + ) @classmethod def from_discrete_values( - cls, - object_type, - sampling_frequency, - channels, - vbr, - bitrate + cls, object_type, sampling_frequency, channels, vbr, bitrate ): return AacMediaCodecInformation( - object_type = cls.OBJECT_TYPE_BITS[object_type], - sampling_frequency = cls.SAMPLING_FREQUENCY_BITS[sampling_frequency], - channels = cls.CHANNELS_BITS[channels], - vbr = vbr, - bitrate = bitrate + object_type=cls.OBJECT_TYPE_BITS[object_type], + sampling_frequency=cls.SAMPLING_FREQUENCY_BITS[sampling_frequency], + channels=cls.CHANNELS_BITS[channels], + rfa=0, + vbr=vbr, + bitrate=bitrate, ) @classmethod - def from_lists( - cls, - object_types, - sampling_frequencies, - channels, - vbr, - bitrate - ): + def from_lists(cls, object_types, sampling_frequencies, channels, vbr, bitrate): return AacMediaCodecInformation( - object_type = sum(cls.OBJECT_TYPE_BITS[x] for x in object_types), - sampling_frequency = sum(cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies), - channels = sum(cls.CHANNELS_BITS[x] for x in channels), - vbr = vbr, - bitrate = bitrate + object_type=sum(cls.OBJECT_TYPE_BITS[x] for x in object_types), + sampling_frequency=sum( + cls.SAMPLING_FREQUENCY_BITS[x] for x in sampling_frequencies + ), + channels=sum(cls.CHANNELS_BITS[x] for x in channels), + vbr=vbr, + bitrate=bitrate, ) - def __bytes__(self): - return bitstruct.pack(self.BIT_FIELDS, *self) + def __bytes__(self) -> bytes: + return bytes( + [ + self.object_type & 0xFF, + (self.sampling_frequency >> 4) & 0xFF, + (((self.sampling_frequency & 0x0F) << 4) | (self.channels << 2)) & 0xFF, + ((self.vbr << 7) | ((self.bitrate >> 16) & 0x7F)) & 0xFF, + ((self.bitrate >> 8) & 0xFF) & 0xFF, + self.bitrate & 0xFF, + ] + ) def __str__(self): - object_types = ['MPEG_2_AAC_LC', 'MPEG_4_AAC_LC', 'MPEG_4_AAC_LTP', 'MPEG_4_AAC_SCALABLE', '[4]', '[5]', '[6]', '[7]'] + object_types = [ + 'MPEG_2_AAC_LC', + 'MPEG_4_AAC_LC', + 'MPEG_4_AAC_LTP', + 'MPEG_4_AAC_SCALABLE', + '[4]', + '[5]', + '[6]', + '[7]', + ] channels = [1, 2] - return '\n'.join([ - 'AacMediaCodecInformation(', - f' object_type: {",".join([str(x) for x in flags_to_list(self.object_type, object_types)])}', - f' sampling_frequency: {",".join([str(x) for x in flags_to_list(self.sampling_frequency, MPEG_2_4_AAC_SAMPLING_FREQUENCIES)])}', - f' channels: {",".join([str(x) for x in flags_to_list(self.channels, channels)])}', - f' vbr: {self.vbr}', - f' bitrate: {self.bitrate}' - ')' - ]) + # pylint: disable=line-too-long + return '\n'.join( + [ + 'AacMediaCodecInformation(', + f' object_type: {",".join([str(x) for x in flags_to_list(self.object_type, object_types)])}', + f' sampling_frequency: {",".join([str(x) for x in flags_to_list(self.sampling_frequency, MPEG_2_4_AAC_SAMPLING_FREQUENCIES)])}', + f' channels: {",".join([str(x) for x in flags_to_list(self.channels, channels)])}', + f' vbr: {self.vbr}', + f' bitrate: {self.bitrate}' ')', + ] + ) # ----------------------------------------------------------------------------- @@ -418,37 +486,34 @@ class VendorSpecificMediaCodecInformation: def __init__(self, vendor_id, codec_id, value): self.vendor_id = vendor_id - self.codec_id = codec_id - self.value = value + self.codec_id = codec_id + self.value = value def __bytes__(self): return struct.pack('<IH', self.vendor_id, self.codec_id, self.value) def __str__(self): - return '\n'.join([ - 'VendorSpecificMediaCodecInformation(', - f' vendor_id: {self.vendor_id:08X} ({name_or_number(COMPANY_IDENTIFIERS, self.vendor_id & 0xFFFF)})', - f' codec_id: {self.codec_id:04X}', - f' value: {self.value.hex()}' - ')' - ]) + # pylint: disable=line-too-long + return '\n'.join( + [ + 'VendorSpecificMediaCodecInformation(', + f' vendor_id: {self.vendor_id:08X} ({name_or_number(COMPANY_IDENTIFIERS, self.vendor_id & 0xFFFF)})', + f' codec_id: {self.codec_id:04X}', + f' value: {self.value.hex()}' ')', + ] + ) # ----------------------------------------------------------------------------- class SbcFrame: def __init__( - self, - sampling_frequency, - block_count, - channel_mode, - subband_count, - payload + self, sampling_frequency, block_count, channel_mode, subband_count, payload ): self.sampling_frequency = sampling_frequency - self.block_count = block_count - self.channel_mode = channel_mode - self.subband_count = subband_count - self.payload = payload + self.block_count = block_count + self.channel_mode = channel_mode + self.subband_count = subband_count + self.payload = payload @property def sample_count(self): @@ -463,7 +528,13 @@ class SbcFrame: return self.sample_count / self.sampling_frequency def __str__(self): - return f'SBC(sf={self.sampling_frequency},cm={self.channel_mode},br={self.bitrate},sc={self.sample_count},size={len(self.payload)})' + return ( + f'SBC(sf={self.sampling_frequency},' + f'cm={self.channel_mode},' + f'br={self.bitrate},' + f'sc={self.sample_count},' + f'size={len(self.payload)})' + ) # ----------------------------------------------------------------------------- @@ -487,24 +558,30 @@ class SbcParser: # Extract some of the header fields sampling_frequency = SBC_SAMPLING_FREQUENCIES[(header[1] >> 6) & 3] - blocks = 4 * (1 + ((header[1] >> 4) & 3)) - channel_mode = (header[1] >> 2) & 3 - channels = 1 if channel_mode == SBC_MONO_CHANNEL_MODE else 2 - subbands = 8 if ((header[1]) & 1) else 4 - bitpool = header[2] + blocks = 4 * (1 + ((header[1] >> 4) & 3)) + channel_mode = (header[1] >> 2) & 3 + channels = 1 if channel_mode == SBC_MONO_CHANNEL_MODE else 2 + subbands = 8 if ((header[1]) & 1) else 4 + bitpool = header[2] # Compute the frame length frame_length = 4 + (4 * subbands * channels) // 8 if channel_mode in (SBC_MONO_CHANNEL_MODE, SBC_DUAL_CHANNEL_MODE): frame_length += (blocks * channels * bitpool) // 8 else: - frame_length += ((1 if channel_mode == SBC_JOINT_STEREO_CHANNEL_MODE else 0) * subbands + blocks * bitpool) // 8 + frame_length += ( + (1 if channel_mode == SBC_JOINT_STEREO_CHANNEL_MODE else 0) + * subbands + + blocks * bitpool + ) // 8 # Read the rest of the frame payload = header + await self.read(frame_length - 4) # Emit the next frame - yield SbcFrame(sampling_frequency, blocks, channel_mode, subbands, payload) + yield SbcFrame( + sampling_frequency, blocks, channel_mode, subbands, payload + ) return generate_frames() @@ -512,19 +589,20 @@ class SbcParser: # ----------------------------------------------------------------------------- class SbcPacketSource: def __init__(self, read, mtu, codec_capabilities): - self.read = read - self.mtu = mtu + self.read = read + self.mtu = mtu self.codec_capabilities = codec_capabilities @property def packets(self): async def generate_packets(): + # pylint: disable=import-outside-toplevel from .avdtp import MediaPacket # Import here to avoid a circular reference sequence_number = 0 - timestamp = 0 - frames = [] - frames_size = 0 + timestamp = 0 + frames = [] + frames_size = 0 max_rtp_payload = self.mtu - 12 - 1 # NOTE: this doesn't support frame fragments @@ -532,18 +610,25 @@ class SbcPacketSource: async for frame in sbc_parser.frames: print(frame) - if frames_size + len(frame.payload) > max_rtp_payload or len(frames) == 16: + if ( + frames_size + len(frame.payload) > max_rtp_payload + or len(frames) == 16 + ): # Need to flush what has been accumulated so far # Emit a packet - sbc_payload = bytes([len(frames)]) + b''.join([frame.payload for frame in frames]) - packet = MediaPacket(2, 0, 0, 0, sequence_number, timestamp, 0, [], 96, sbc_payload) + sbc_payload = bytes([len(frames)]) + b''.join( + [frame.payload for frame in frames] + ) + packet = MediaPacket( + 2, 0, 0, 0, sequence_number, timestamp, 0, [], 96, sbc_payload + ) packet.timestamp_seconds = timestamp / frame.sampling_frequency yield packet # Prepare for next packets sequence_number += 1 - timestamp += sum([frame.sample_count for frame in frames]) + timestamp += sum((frame.sample_count for frame in frames)) frames = [frame] frames_size = len(frame.payload) else: diff --git a/bumble/att.py b/bumble/att.py index 22b683e..55ae8a5 100644 --- a/bumble/att.py +++ b/bumble/att.py @@ -22,15 +22,25 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- -from colors import color +from __future__ import annotations +import functools +import struct from pyee import EventEmitter +from typing import Dict, Type, TYPE_CHECKING -from .core import * -from .hci import * +from bumble.core import UUID, name_or_number, get_dict_key_by_value, ProtocolError +from bumble.hci import HCI_Object, key_with_value, HCI_Constant +from bumble.colors import color + +if TYPE_CHECKING: + from bumble.device import Connection # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off +# pylint: disable=line-too-long + ATT_CID = 0x04 ATT_ERROR_RESPONSE = 0x01 @@ -163,30 +173,30 @@ ATT_ERROR_NAMES = { ATT_DEFAULT_MTU = 23 HANDLE_FIELD_SPEC = {'size': 2, 'mapper': lambda x: f'0x{x:04X}'} -UUID_2_16_FIELD_SPEC = lambda x, y: UUID.parse_uuid(x, y) # noqa: E731 +# pylint: disable-next=unnecessary-lambda-assignment,unnecessary-lambda +UUID_2_16_FIELD_SPEC = lambda x, y: UUID.parse_uuid(x, y) +# pylint: disable-next=unnecessary-lambda-assignment,unnecessary-lambda UUID_2_FIELD_SPEC = lambda x, y: UUID.parse_uuid_2(x, y) # noqa: E731 - -# ----------------------------------------------------------------------------- -# Utils -# ----------------------------------------------------------------------------- -def key_with_value(dictionary, target_value): - for key, value in dictionary.items(): - if value == target_value: - return key - return None - +# fmt: on +# pylint: enable=line-too-long +# pylint: disable=invalid-name # ----------------------------------------------------------------------------- # Exceptions # ----------------------------------------------------------------------------- -class ATT_Error(Exception): - def __init__(self, error_code, att_handle=0x0000): - self.error_code = error_code +class ATT_Error(ProtocolError): + def __init__(self, error_code, att_handle=0x0000, message=''): + super().__init__( + error_code, + error_namespace='att', + error_name=ATT_PDU.error_name(error_code), + ) self.att_handle = att_handle + self.message = message def __str__(self): - return f'ATT_Error({ATT_PDU.error_name(self.error_code)})' + return f'ATT_Error(error={self.error_name}, handle={self.att_handle:04X}): {self.message}' # ----------------------------------------------------------------------------- @@ -196,8 +206,10 @@ class ATT_PDU: ''' See Bluetooth spec @ Vol 3, Part F - 3.3 ATTRIBUTE PDU ''' - pdu_classes = {} + + pdu_classes: Dict[int, Type[ATT_PDU]] = {} op_code = 0 + name = None @staticmethod def from_bytes(pdu): @@ -274,11 +286,13 @@ class ATT_PDU: # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('request_opcode_in_error', {'size': 1, 'mapper': ATT_PDU.pdu_name}), - ('attribute_handle_in_error', HANDLE_FIELD_SPEC), - ('error_code', {'size': 1, 'mapper': ATT_PDU.error_name}) -]) +@ATT_PDU.subclass( + [ + ('request_opcode_in_error', {'size': 1, 'mapper': ATT_PDU.pdu_name}), + ('attribute_handle_in_error', HANDLE_FIELD_SPEC), + ('error_code', {'size': 1, 'mapper': ATT_PDU.error_name}), + ] +) class ATT_Error_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.1.1 Error Response @@ -286,9 +300,7 @@ class ATT_Error_Response(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('client_rx_mtu', 2) -]) +@ATT_PDU.subclass([('client_rx_mtu', 2)]) class ATT_Exchange_MTU_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.2.1 Exchange MTU Request @@ -296,9 +308,7 @@ class ATT_Exchange_MTU_Request(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('server_rx_mtu', 2) -]) +@ATT_PDU.subclass([('server_rx_mtu', 2)]) class ATT_Exchange_MTU_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.2.2 Exchange MTU Response @@ -306,10 +316,9 @@ class ATT_Exchange_MTU_Response(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('starting_handle', HANDLE_FIELD_SPEC), - ('ending_handle', HANDLE_FIELD_SPEC) -]) +@ATT_PDU.subclass( + [('starting_handle', HANDLE_FIELD_SPEC), ('ending_handle', HANDLE_FIELD_SPEC)] +) class ATT_Find_Information_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.3.1 Find Information Request @@ -317,10 +326,7 @@ class ATT_Find_Information_Request(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('format', 1), - ('information_data', '*') -]) +@ATT_PDU.subclass([('format', 1), ('information_data', '*')]) class ATT_Find_Information_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.3.2 Find Information Response @@ -332,7 +338,7 @@ class ATT_Find_Information_Response(ATT_PDU): uuid_size = 2 if self.format == 1 else 16 while offset + uuid_size <= len(self.information_data): handle = struct.unpack_from('<H', self.information_data, offset)[0] - uuid = self.information_data[2 + offset:2 + offset + uuid_size] + uuid = self.information_data[2 + offset : 2 + offset + uuid_size] self.information.append((handle, uuid)) offset += 2 + uuid_size @@ -346,20 +352,33 @@ class ATT_Find_Information_Response(ATT_PDU): def __str__(self): result = color(self.name, 'yellow') - result += ':\n' + HCI_Object.format_fields(self.__dict__, [ - ('format', 1), - ('information', {'mapper': lambda x: ', '.join([f'0x{handle:04X}:{uuid.hex()}' for handle, uuid in x])}) - ], ' ') + result += ':\n' + HCI_Object.format_fields( + self.__dict__, + [ + ('format', 1), + ( + 'information', + { + 'mapper': lambda x: ', '.join( + [f'0x{handle:04X}:{uuid.hex()}' for handle, uuid in x] + ) + }, + ), + ], + ' ', + ) return result # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('starting_handle', HANDLE_FIELD_SPEC), - ('ending_handle', HANDLE_FIELD_SPEC), - ('attribute_type', UUID_2_FIELD_SPEC), - ('attribute_value', '*') -]) +@ATT_PDU.subclass( + [ + ('starting_handle', HANDLE_FIELD_SPEC), + ('ending_handle', HANDLE_FIELD_SPEC), + ('attribute_type', UUID_2_FIELD_SPEC), + ('attribute_value', '*'), + ] +) class ATT_Find_By_Type_Value_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.3.3 Find By Type Value Request @@ -367,9 +386,7 @@ class ATT_Find_By_Type_Value_Request(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('handles_information_list', '*') -]) +@ATT_PDU.subclass([('handles_information_list', '*')]) class ATT_Find_By_Type_Value_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.3.4 Find By Type Value Response @@ -379,7 +396,9 @@ class ATT_Find_By_Type_Value_Response(ATT_PDU): self.handles_information = [] offset = 0 while offset + 4 <= len(self.handles_information_list): - found_attribute_handle, group_end_handle = struct.unpack_from('<HH', self.handles_information_list, offset) + found_attribute_handle, group_end_handle = struct.unpack_from( + '<HH', self.handles_information_list, offset + ) self.handles_information.append((found_attribute_handle, group_end_handle)) offset += 4 @@ -393,18 +412,34 @@ class ATT_Find_By_Type_Value_Response(ATT_PDU): def __str__(self): result = color(self.name, 'yellow') - result += ':\n' + HCI_Object.format_fields(self.__dict__, [ - ('handles_information', {'mapper': lambda x: ', '.join([f'0x{handle1:04X}-0x{handle2:04X}' for handle1, handle2 in x])}) - ], ' ') + result += ':\n' + HCI_Object.format_fields( + self.__dict__, + [ + ( + 'handles_information', + { + 'mapper': lambda x: ', '.join( + [ + f'0x{handle1:04X}-0x{handle2:04X}' + for handle1, handle2 in x + ] + ) + }, + ) + ], + ' ', + ) return result # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('starting_handle', HANDLE_FIELD_SPEC), - ('ending_handle', HANDLE_FIELD_SPEC), - ('attribute_type', UUID_2_16_FIELD_SPEC) -]) +@ATT_PDU.subclass( + [ + ('starting_handle', HANDLE_FIELD_SPEC), + ('ending_handle', HANDLE_FIELD_SPEC), + ('attribute_type', UUID_2_16_FIELD_SPEC), + ] +) class ATT_Read_By_Type_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.1 Read By Type Request @@ -412,10 +447,7 @@ class ATT_Read_By_Type_Request(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('length', 1), - ('attribute_data_list', '*') -]) +@ATT_PDU.subclass([('length', 1), ('attribute_data_list', '*')]) class ATT_Read_By_Type_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.2 Read By Type Response @@ -424,9 +456,15 @@ class ATT_Read_By_Type_Response(ATT_PDU): def parse_attribute_data_list(self): self.attributes = [] offset = 0 - while self.length != 0 and offset + self.length <= len(self.attribute_data_list): - attribute_handle, = struct.unpack_from('<H', self.attribute_data_list, offset) - attribute_value = self.attribute_data_list[offset + 2:offset + self.length] + while self.length != 0 and offset + self.length <= len( + self.attribute_data_list + ): + (attribute_handle,) = struct.unpack_from( + '<H', self.attribute_data_list, offset + ) + attribute_value = self.attribute_data_list[ + offset + 2 : offset + self.length + ] self.attributes.append((attribute_handle, attribute_value)) offset += self.length @@ -440,17 +478,26 @@ class ATT_Read_By_Type_Response(ATT_PDU): def __str__(self): result = color(self.name, 'yellow') - result += ':\n' + HCI_Object.format_fields(self.__dict__, [ - ('length', 1), - ('attributes', {'mapper': lambda x: ', '.join([f'0x{handle:04X}:{value.hex()}' for handle, value in x])}) - ], ' ') + result += ':\n' + HCI_Object.format_fields( + self.__dict__, + [ + ('length', 1), + ( + 'attributes', + { + 'mapper': lambda x: ', '.join( + [f'0x{handle:04X}:{value.hex()}' for handle, value in x] + ) + }, + ), + ], + ' ', + ) return result # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_handle', HANDLE_FIELD_SPEC) -]) +@ATT_PDU.subclass([('attribute_handle', HANDLE_FIELD_SPEC)]) class ATT_Read_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.3 Read Request @@ -458,9 +505,7 @@ class ATT_Read_Request(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_value', '*') -]) +@ATT_PDU.subclass([('attribute_value', '*')]) class ATT_Read_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.4 Read Response @@ -468,10 +513,7 @@ class ATT_Read_Response(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_handle', HANDLE_FIELD_SPEC), - ('value_offset', 2) -]) +@ATT_PDU.subclass([('attribute_handle', HANDLE_FIELD_SPEC), ('value_offset', 2)]) class ATT_Read_Blob_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.5 Read Blob Request @@ -479,9 +521,7 @@ class ATT_Read_Blob_Request(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('part_attribute_value', '*') -]) +@ATT_PDU.subclass([('part_attribute_value', '*')]) class ATT_Read_Blob_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.6 Read Blob Response @@ -489,9 +529,7 @@ class ATT_Read_Blob_Response(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('set_of_handles', '*') -]) +@ATT_PDU.subclass([('set_of_handles', '*')]) class ATT_Read_Multiple_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.7 Read Multiple Request @@ -499,9 +537,7 @@ class ATT_Read_Multiple_Request(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('set_of_values', '*') -]) +@ATT_PDU.subclass([('set_of_values', '*')]) class ATT_Read_Multiple_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.8 Read Multiple Response @@ -509,11 +545,13 @@ class ATT_Read_Multiple_Response(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('starting_handle', HANDLE_FIELD_SPEC), - ('ending_handle', HANDLE_FIELD_SPEC), - ('attribute_group_type', UUID_2_16_FIELD_SPEC) -]) +@ATT_PDU.subclass( + [ + ('starting_handle', HANDLE_FIELD_SPEC), + ('ending_handle', HANDLE_FIELD_SPEC), + ('attribute_group_type', UUID_2_16_FIELD_SPEC), + ] +) class ATT_Read_By_Group_Type_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.9 Read by Group Type Request @@ -521,10 +559,7 @@ class ATT_Read_By_Group_Type_Request(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('length', 1), - ('attribute_data_list', '*') -]) +@ATT_PDU.subclass([('length', 1), ('attribute_data_list', '*')]) class ATT_Read_By_Group_Type_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.4.10 Read by Group Type Response @@ -533,10 +568,18 @@ class ATT_Read_By_Group_Type_Response(ATT_PDU): def parse_attribute_data_list(self): self.attributes = [] offset = 0 - while self.length != 0 and offset + self.length <= len(self.attribute_data_list): - attribute_handle, end_group_handle = struct.unpack_from('<HH', self.attribute_data_list, offset) - attribute_value = self.attribute_data_list[offset + 4:offset + self.length] - self.attributes.append((attribute_handle, end_group_handle, attribute_value)) + while self.length != 0 and offset + self.length <= len( + self.attribute_data_list + ): + attribute_handle, end_group_handle = struct.unpack_from( + '<HH', self.attribute_data_list, offset + ) + attribute_value = self.attribute_data_list[ + offset + 4 : offset + self.length + ] + self.attributes.append( + (attribute_handle, end_group_handle, attribute_value) + ) offset += self.length def __init__(self, *args, **kwargs): @@ -549,18 +592,29 @@ class ATT_Read_By_Group_Type_Response(ATT_PDU): def __str__(self): result = color(self.name, 'yellow') - result += ':\n' + HCI_Object.format_fields(self.__dict__, [ - ('length', 1), - ('attributes', {'mapper': lambda x: ', '.join([f'0x{handle:04X}-0x{end:04X}:{value.hex()}' for handle, end, value in x])}) - ], ' ') + result += ':\n' + HCI_Object.format_fields( + self.__dict__, + [ + ('length', 1), + ( + 'attributes', + { + 'mapper': lambda x: ', '.join( + [ + f'0x{handle:04X}-0x{end:04X}:{value.hex()}' + for handle, end, value in x + ] + ) + }, + ), + ], + ' ', + ) return result # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_handle', HANDLE_FIELD_SPEC), - ('attribute_value', '*') -]) +@ATT_PDU.subclass([('attribute_handle', HANDLE_FIELD_SPEC), ('attribute_value', '*')]) class ATT_Write_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.5.1 Write Request @@ -576,10 +630,7 @@ class ATT_Write_Response(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_handle', HANDLE_FIELD_SPEC), - ('attribute_value', '*') -]) +@ATT_PDU.subclass([('attribute_handle', HANDLE_FIELD_SPEC), ('attribute_value', '*')]) class ATT_Write_Command(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.5.3 Write Command @@ -587,11 +638,13 @@ class ATT_Write_Command(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_handle', HANDLE_FIELD_SPEC), - ('attribute_value', '*') - # ('authentication_signature', 'TODO') -]) +@ATT_PDU.subclass( + [ + ('attribute_handle', HANDLE_FIELD_SPEC), + ('attribute_value', '*') + # ('authentication_signature', 'TODO') + ] +) class ATT_Signed_Write_Command(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.5.4 Signed Write Command @@ -599,11 +652,13 @@ class ATT_Signed_Write_Command(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_handle', HANDLE_FIELD_SPEC), - ('value_offset', 2), - ('part_attribute_value', '*') -]) +@ATT_PDU.subclass( + [ + ('attribute_handle', HANDLE_FIELD_SPEC), + ('value_offset', 2), + ('part_attribute_value', '*'), + ] +) class ATT_Prepare_Write_Request(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.6.1 Prepare Write Request @@ -611,11 +666,13 @@ class ATT_Prepare_Write_Request(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_handle', HANDLE_FIELD_SPEC), - ('value_offset', 2), - ('part_attribute_value', '*') -]) +@ATT_PDU.subclass( + [ + ('attribute_handle', HANDLE_FIELD_SPEC), + ('value_offset', 2), + ('part_attribute_value', '*'), + ] +) class ATT_Prepare_Write_Response(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.6.2 Prepare Write Response @@ -639,10 +696,7 @@ class ATT_Execute_Write_Response(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_handle', HANDLE_FIELD_SPEC), - ('attribute_value', '*') -]) +@ATT_PDU.subclass([('attribute_handle', HANDLE_FIELD_SPEC), ('attribute_value', '*')]) class ATT_Handle_Value_Notification(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.7.1 Handle Value Notification @@ -650,10 +704,7 @@ class ATT_Handle_Value_Notification(ATT_PDU): # ----------------------------------------------------------------------------- -@ATT_PDU.subclass([ - ('attribute_handle', HANDLE_FIELD_SPEC), - ('attribute_value', '*') -]) +@ATT_PDU.subclass([('attribute_handle', HANDLE_FIELD_SPEC), ('attribute_value', '*')]) class ATT_Handle_Value_Indication(ATT_PDU): ''' See Bluetooth spec @ Vol 3, Part F - 3.4.7.2 Handle Value Indication @@ -671,58 +722,143 @@ class ATT_Handle_Value_Confirmation(ATT_PDU): # ----------------------------------------------------------------------------- class Attribute(EventEmitter): # Permission flags - READABLE = 0x01 - WRITEABLE = 0x02 - READ_REQUIRES_ENCRYPTION = 0x04 - WRITE_REQUIRES_ENCRYPTION = 0x08 - READ_REQUIRES_AUTHENTICATION = 0x10 + READABLE = 0x01 + WRITEABLE = 0x02 + READ_REQUIRES_ENCRYPTION = 0x04 + WRITE_REQUIRES_ENCRYPTION = 0x08 + READ_REQUIRES_AUTHENTICATION = 0x10 WRITE_REQUIRES_AUTHENTICATION = 0x20 - READ_REQUIRES_AUTHORIZATION = 0x40 - WRITE_REQUIRES_AUTHORIZATION = 0x80 + READ_REQUIRES_AUTHORIZATION = 0x40 + WRITE_REQUIRES_AUTHORIZATION = 0x80 + + PERMISSION_NAMES = { + READABLE: 'READABLE', + WRITEABLE: 'WRITEABLE', + READ_REQUIRES_ENCRYPTION: 'READ_REQUIRES_ENCRYPTION', + WRITE_REQUIRES_ENCRYPTION: 'WRITE_REQUIRES_ENCRYPTION', + READ_REQUIRES_AUTHENTICATION: 'READ_REQUIRES_AUTHENTICATION', + WRITE_REQUIRES_AUTHENTICATION: 'WRITE_REQUIRES_AUTHENTICATION', + READ_REQUIRES_AUTHORIZATION: 'READ_REQUIRES_AUTHORIZATION', + WRITE_REQUIRES_AUTHORIZATION: 'WRITE_REQUIRES_AUTHORIZATION', + } - def __init__(self, attribute_type, permissions, value = b''): + @staticmethod + def string_to_permissions(permissions_str: str): + try: + return functools.reduce( + lambda x, y: x | get_dict_key_by_value(Attribute.PERMISSION_NAMES, y), + permissions_str.split(","), + 0, + ) + except TypeError as exc: + raise TypeError( + f"Attribute::permissions error:\nExpected a string containing any of the keys, separated by commas: {','.join(Attribute.PERMISSION_NAMES.values())}\nGot: {permissions_str}" + ) from exc + + def __init__(self, attribute_type, permissions, value=b''): EventEmitter.__init__(self) - self.handle = 0 + self.handle = 0 self.end_group_handle = 0 - self.permissions = permissions + if isinstance(permissions, str): + self.permissions = self.string_to_permissions(permissions) + else: + self.permissions = permissions # Convert the type to a UUID object if it isn't already - if type(attribute_type) is str: + if isinstance(attribute_type, str): self.type = UUID(attribute_type) - elif type(attribute_type) is bytes: + elif isinstance(attribute_type, bytes): self.type = UUID.from_bytes(attribute_type) else: self.type = attribute_type # Convert the value to a byte array - if type(value) is str: + if isinstance(value, str): self.value = bytes(value, 'utf-8') else: self.value = value - def read_value(self, connection): + def encode_value(self, value): + return value + + def decode_value(self, value_bytes): + return value_bytes + + def read_value(self, connection: Connection): + if ( + self.permissions & self.READ_REQUIRES_ENCRYPTION + ) and not connection.encryption: + raise ATT_Error( + error_code=ATT_INSUFFICIENT_ENCRYPTION_ERROR, att_handle=self.handle + ) + if ( + self.permissions & self.READ_REQUIRES_AUTHENTICATION + ) and not connection.authenticated: + raise ATT_Error( + error_code=ATT_INSUFFICIENT_AUTHENTICATION_ERROR, att_handle=self.handle + ) + if self.permissions & self.READ_REQUIRES_AUTHORIZATION: + # TODO: handle authorization better + raise ATT_Error( + error_code=ATT_INSUFFICIENT_AUTHORIZATION_ERROR, att_handle=self.handle + ) + if read := getattr(self.value, 'read', None): try: - return read(connection) + value = read(connection) # pylint: disable=not-callable except ATT_Error as error: - raise ATT_Error(error_code=error.error_code, att_handle=self.handle) + raise ATT_Error( + error_code=error.error_code, att_handle=self.handle + ) from error else: - return self.value + value = self.value + + return self.encode_value(value) + + def write_value(self, connection: Connection, value_bytes): + if ( + self.permissions & self.WRITE_REQUIRES_ENCRYPTION + ) and not connection.encryption: + raise ATT_Error( + error_code=ATT_INSUFFICIENT_ENCRYPTION_ERROR, att_handle=self.handle + ) + if ( + self.permissions & self.WRITE_REQUIRES_AUTHENTICATION + ) and not connection.authenticated: + raise ATT_Error( + error_code=ATT_INSUFFICIENT_AUTHENTICATION_ERROR, att_handle=self.handle + ) + if self.permissions & self.WRITE_REQUIRES_AUTHORIZATION: + # TODO: handle authorization better + raise ATT_Error( + error_code=ATT_INSUFFICIENT_AUTHORIZATION_ERROR, att_handle=self.handle + ) + + value = self.decode_value(value_bytes) - def write_value(self, connection, value): if write := getattr(self.value, 'write', None): try: - write(connection, value) + write(connection, value) # pylint: disable=not-callable except ATT_Error as error: - raise ATT_Error(error_code=error.error_code, att_handle=self.handle) + raise ATT_Error( + error_code=error.error_code, att_handle=self.handle + ) from error else: self.value = value self.emit('write', connection, value) def __repr__(self): - if len(self.value) > 0: + if isinstance(self.value, bytes): + value_str = self.value.hex() + else: + value_str = str(self.value) + if value_str: value_string = f', value={self.value.hex()}' else: value_string = '' - return f'Attribute(handle=0x{self.handle:04X}, type={self.type}, permissions={self.permissions}{value_string})' + return ( + f'Attribute(handle=0x{self.handle:04X}, ' + f'type={self.type}, ' + f'permissions={self.permissions}{value_string})' + ) diff --git a/bumble/avdtp.py b/bumble/avdtp.py index 759e38c..238036d 100644 --- a/bumble/avdtp.py +++ b/bumble/avdtp.py @@ -15,18 +15,19 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import asyncio import struct import time import logging -from colors import color from pyee import EventEmitter +from typing import Dict, Type from .core import ( BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE, InvalidStateError, ProtocolError, - name_or_number + name_or_number, ) from .a2dp import ( A2DP_CODEC_TYPE_NAMES, @@ -35,9 +36,10 @@ from .a2dp import ( A2DP_SBC_CODEC_TYPE, AacMediaCodecInformation, SbcMediaCodecInformation, - VendorSpecificMediaCodecInformation + VendorSpecificMediaCodecInformation, ) from . import sdp +from .colors import color # ----------------------------------------------------------------------------- # Logging @@ -48,6 +50,9 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off +# pylint: disable=line-too-long + AVDTP_PSM = 0x0019 AVDTP_DEFAULT_RTX_SIG_TIMER = 5 # Seconds @@ -195,6 +200,10 @@ AVDTP_STATE_NAMES = { AVDTP_ABORTING_STATE: 'AVDTP_ABORTING_STATE' } +# fmt: on +# pylint: enable=line-too-long +# pylint: disable=invalid-name + # ----------------------------------------------------------------------------- async def find_avdtp_service_with_sdp_client(sdp_client): @@ -206,14 +215,11 @@ async def find_avdtp_service_with_sdp_client(sdp_client): # Search for services with an Audio Sink service class search_result = await sdp_client.search_attributes( [BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE], - [ - sdp.SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID - ] + [sdp.SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID], ) for attribute_list in search_result: profile_descriptor_list = sdp.ServiceAttribute.find_attribute_in_list( - attribute_list, - sdp.SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID + attribute_list, sdp.SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID ) if profile_descriptor_list: for profile_descriptor in profile_descriptor_list.value: @@ -251,17 +257,19 @@ class RealtimeClock: class MediaPacket: @staticmethod def from_bytes(data): - version = (data[0] >> 6) & 0x03 - padding = (data[0] >> 5) & 0x01 - extension = (data[0] >> 4) & 0x01 - csrc_count = data[0] & 0x0F - marker = (data[1] >> 7) & 0x01 - payload_type = data[1] & 0x7F + version = (data[0] >> 6) & 0x03 + padding = (data[0] >> 5) & 0x01 + extension = (data[0] >> 4) & 0x01 + csrc_count = data[0] & 0x0F + marker = (data[1] >> 7) & 0x01 + payload_type = data[1] & 0x7F sequence_number = struct.unpack_from('>H', data, 2)[0] - timestamp = struct.unpack_from('>I', data, 4)[0] - ssrc = struct.unpack_from('>I', data, 8)[0] - csrc_list = [struct.unpack_from('>I', data, 12 + i)[0] for i in range(csrc_count)] - payload = data[12 + csrc_count * 4:] + timestamp = struct.unpack_from('>I', data, 4)[0] + ssrc = struct.unpack_from('>I', data, 8)[0] + csrc_list = [ + struct.unpack_from('>I', data, 12 + i)[0] for i in range(csrc_count) + ] + payload = data[12 + csrc_count * 4 :] return MediaPacket( version, @@ -273,7 +281,7 @@ class MediaPacket: ssrc, csrc_list, payload_type, - payload + payload, ) def __init__( @@ -287,45 +295,58 @@ class MediaPacket: ssrc, csrc_list, payload_type, - payload + payload, ): - self.version = version - self.padding = padding - self.extension = extension - self.marker = marker + self.version = version + self.padding = padding + self.extension = extension + self.marker = marker self.sequence_number = sequence_number - self.timestamp = timestamp - self.ssrc = ssrc - self.csrc_list = csrc_list - self.payload_type = payload_type - self.payload = payload + self.timestamp = timestamp + self.ssrc = ssrc + self.csrc_list = csrc_list + self.payload_type = payload_type + self.payload = payload def __bytes__(self): - header = ( - bytes([ - self.version << 6 | self.padding << 5 | self.extension << 4 | len(self.csrc_list), - self.marker << 7 | self.payload_type - ]) + - struct.pack('>HII', self.sequence_number, self.timestamp, self.ssrc) - ) + header = bytes( + [ + self.version << 6 + | self.padding << 5 + | self.extension << 4 + | len(self.csrc_list), + self.marker << 7 | self.payload_type, + ] + ) + struct.pack('>HII', self.sequence_number, self.timestamp, self.ssrc) for csrc in self.csrc_list: header += struct.pack('>I', csrc) return header + self.payload def __str__(self): - return f'RTP(v={self.version},p={self.padding},x={self.extension},m={self.marker},pt={self.payload_type},sn={self.sequence_number},ts={self.timestamp},ssrc={self.ssrc},csrcs={self.csrc_list},payload_size={len(self.payload)})' + return ( + f'RTP(v={self.version},' + f'p={self.padding},' + f'x={self.extension},' + f'm={self.marker},' + f'pt={self.payload_type},' + f'sn={self.sequence_number},' + f'ts={self.timestamp},' + f'ssrc={self.ssrc},' + f'csrcs={self.csrc_list},' + f'payload_size={len(self.payload)})' + ) # ----------------------------------------------------------------------------- class MediaPacketPump: def __init__(self, packets, clock=RealtimeClock()): - self.packets = packets - self.clock = clock + self.packets = packets + self.clock = clock self.pump_task = None async def start(self, rtp_channel): async def pump_packets(): - start_time = 0 + start_time = 0 start_timestamp = 0 try: @@ -333,7 +354,7 @@ class MediaPacketPump: async for packet in self.packets: # Capture the timestamp of the first packet if start_time == 0: - start_time = self.clock.now() + start_time = self.clock.now() start_timestamp = packet.timestamp_seconds # Wait until we can send @@ -346,12 +367,14 @@ class MediaPacketPump: # Emit rtp_channel.send_pdu(bytes(packet)) - logger.debug(f'{color(">>> sending RTP packet:", "green")} {packet}') + logger.debug( + f'{color(">>> sending RTP packet:", "green")} {packet}' + ) except asyncio.exceptions.CancelledError: logger.debug('pump canceled') # Pump packets - self.pump_task = asyncio.get_running_loop().create_task(pump_packets()) + self.pump_task = asyncio.create_task(pump_packets()) async def stop(self): # Stop the pump @@ -362,73 +385,96 @@ class MediaPacketPump: # ----------------------------------------------------------------------------- -class MessageAssembler: +class MessageAssembler: # pylint: disable=attribute-defined-outside-init def __init__(self, callback): self.callback = callback self.reset() def reset(self): - self.transaction_label = 0 - self.message = None - self.message_type = 0 - self.signal_identifier = 0 + self.transaction_label = 0 + self.message = None + self.message_type = 0 + self.signal_identifier = 0 self.number_of_signal_packets = 0 - self.packet_count = 0 + self.packet_count = 0 def on_pdu(self, pdu): self.packet_count += 1 transaction_label = pdu[0] >> 4 - packet_type = (pdu[0] >> 2) & 3 - message_type = pdu[0] & 3 + packet_type = (pdu[0] >> 2) & 3 + message_type = pdu[0] & 3 - logger.debug(f'transaction_label={transaction_label}, packet_type={Protocol.packet_type_name(packet_type)}, message_type={Message.message_type_name(message_type)}') - if packet_type == Protocol.SINGLE_PACKET or packet_type == Protocol.START_PACKET: + logger.debug( + f'transaction_label={transaction_label}, ' + f'packet_type={Protocol.packet_type_name(packet_type)}, ' + f'message_type={Message.message_type_name(message_type)}' + ) + if packet_type in (Protocol.SINGLE_PACKET, Protocol.START_PACKET): if self.message is not None: # The previous message has not been terminated - logger.warning('received a start or single packet when expecting an end or continuation') + logger.warning( + 'received a start or single packet when expecting an end or ' + 'continuation' + ) self.reset() self.transaction_label = transaction_label self.signal_identifier = pdu[1] & 0x3F - self.message_type = message_type + self.message_type = message_type if packet_type == Protocol.SINGLE_PACKET: self.message = pdu[2:] self.on_message_complete() else: self.number_of_signal_packets = pdu[2] - self.message = pdu[3:] - elif packet_type == Protocol.CONTINUE_PACKET or packet_type == Protocol.END_PACKET: + self.message = pdu[3:] + elif packet_type in (Protocol.CONTINUE_PACKET, Protocol.END_PACKET): if self.packet_count == 0: logger.warning('unexpected continuation') return if transaction_label != self.transaction_label: - logger.warning(f'transaction label mismatch: expected {self.transaction_label}, received {transaction_label}') + logger.warning( + f'transaction label mismatch: expected {self.transaction_label}, ' + f'received {transaction_label}' + ) return if message_type != self.message_type: - logger.warning(f'message type mismatch: expected {self.message_type}, received {message_type}') + logger.warning( + f'message type mismatch: expected {self.message_type}, ' + f'received {message_type}' + ) return self.message += pdu[1:] if packet_type == Protocol.END_PACKET: if self.packet_count != self.number_of_signal_packets: - logger.warning(f'incomplete fragmented message: expected {self.number_of_signal_packets} packets, received {self.packet_count}') + logger.warning( + 'incomplete fragmented message: ' + f'expected {self.number_of_signal_packets} packets, ' + f'received {self.packet_count}' + ) self.reset() return self.on_message_complete() else: if self.packet_count > self.number_of_signal_packets: - logger.warning(f'too many packets: expected {self.number_of_signal_packets}, received {self.packet_count}') + logger.warning( + 'too many packets: ' + f'expected {self.number_of_signal_packets}, ' + f'received {self.packet_count}' + ) self.reset() return def on_message_complete(self): - message = Message.create(self.signal_identifier, self.message_type, self.message) + message = Message.create( + self.signal_identifier, self.message_type, self.message + ) try: self.callback(self.transaction_label, message) @@ -460,12 +506,14 @@ class ServiceCapabilities: def parse_capabilities(payload): capabilities = [] while payload: - service_category = payload[0] + service_category = payload[0] length_of_service_capabilities = payload[1] - service_capabilities_bytes = payload[2:2 + length_of_service_capabilities] - capabilities.append(ServiceCapabilities.create(service_category, service_capabilities_bytes)) + service_capabilities_bytes = payload[2 : 2 + length_of_service_capabilities] + capabilities.append( + ServiceCapabilities.create(service_category, service_capabilities_bytes) + ) - payload = payload[2 + length_of_service_capabilities:] + payload = payload[2 + length_of_service_capabilities :] return capabilities @@ -473,21 +521,24 @@ class ServiceCapabilities: def serialize_capabilities(capabilities): serialized = b'' for item in capabilities: - serialized += bytes([ - item.service_category, - len(item.service_capabilities_bytes) - ]) + item.service_capabilities_bytes + serialized += ( + bytes([item.service_category, len(item.service_capabilities_bytes)]) + + item.service_capabilities_bytes + ) return serialized def init_from_bytes(self): pass def __init__(self, service_category, service_capabilities_bytes=b''): - self.service_category = service_category + self.service_category = service_category self.service_capabilities_bytes = service_capabilities_bytes - def to_string(self, details=[]): - attributes = ','.join([name_or_number(AVDTP_SERVICE_CATEGORY_NAMES, self.service_category)] + details) + def to_string(self, details=[]): # pylint: disable=dangerous-default-value + attributes = ','.join( + [name_or_number(AVDTP_SERVICE_CATEGORY_NAMES, self.service_category)] + + details + ) return f'ServiceCapabilities({attributes})' def __str__(self): @@ -501,31 +552,45 @@ class ServiceCapabilities: # ----------------------------------------------------------------------------- class MediaCodecCapabilities(ServiceCapabilities): def init_from_bytes(self): - self.media_type = self.service_capabilities_bytes[0] - self.media_codec_type = self.service_capabilities_bytes[1] + self.media_type = self.service_capabilities_bytes[0] + self.media_codec_type = self.service_capabilities_bytes[1] self.media_codec_information = self.service_capabilities_bytes[2:] if self.media_codec_type == A2DP_SBC_CODEC_TYPE: - self.media_codec_information = SbcMediaCodecInformation.from_bytes(self.media_codec_information) + self.media_codec_information = SbcMediaCodecInformation.from_bytes( + self.media_codec_information + ) elif self.media_codec_type == A2DP_MPEG_2_4_AAC_CODEC_TYPE: - self.media_codec_information = AacMediaCodecInformation.from_bytes(self.media_codec_information) + self.media_codec_information = AacMediaCodecInformation.from_bytes( + self.media_codec_information + ) elif self.media_codec_type == A2DP_NON_A2DP_CODEC_TYPE: - self.media_codec_information = VendorSpecificMediaCodecInformation.from_bytes(self.media_codec_information) + self.media_codec_information = ( + VendorSpecificMediaCodecInformation.from_bytes( + self.media_codec_information + ) + ) def __init__(self, media_type, media_codec_type, media_codec_information): super().__init__( AVDTP_MEDIA_CODEC_SERVICE_CATEGORY, - bytes([media_type, media_codec_type]) + bytes(media_codec_information) + bytes([media_type, media_codec_type]) + bytes(media_codec_information), ) - self.media_type = media_type - self.media_codec_type = media_codec_type + self.media_type = media_type + self.media_codec_type = media_codec_type self.media_codec_information = media_codec_information def __str__(self): + codec_info = ( + self.media_codec_information.hex() + if isinstance(self.media_codec_information, bytes) + else str(self.media_codec_information) + ) + details = [ f'media_type={name_or_number(AVDTP_MEDIA_TYPE_NAMES, self.media_type)}', f'codec={name_or_number(A2DP_CODEC_TYPE_NAMES, self.media_codec_type)}', - f'codec_info={self.media_codec_information.hex() if type(self.media_codec_information) is bytes else str(self.media_codec_information)}' + f'codec_info={codec_info}', ] return self.to_string(details) @@ -535,51 +600,48 @@ class EndPointInfo: @staticmethod def from_bytes(payload): return EndPointInfo( - payload[0] >> 2, - payload[0] >> 1 & 1, - payload[1] >> 4, - payload[1] >> 3 & 1 + payload[0] >> 2, payload[0] >> 1 & 1, payload[1] >> 4, payload[1] >> 3 & 1 ) def __bytes__(self): - return bytes([ - self.seid << 2 | self.in_use << 1, - self.media_type << 4 | self.tsep << 3 - ]) + return bytes( + [self.seid << 2 | self.in_use << 1, self.media_type << 4 | self.tsep << 3] + ) def __init__(self, seid, in_use, media_type, tsep): - self.seid = seid - self.in_use = in_use + self.seid = seid + self.in_use = in_use self.media_type = media_type - self.tsep = tsep + self.tsep = tsep # ----------------------------------------------------------------------------- -class Message: - COMMAND = 0 - GENERAL_REJECT = 1 +class Message: # pylint:disable=attribute-defined-outside-init + COMMAND = 0 + GENERAL_REJECT = 1 RESPONSE_ACCEPT = 2 RESPONSE_REJECT = 3 MESSAGE_TYPE_NAMES = { - COMMAND: 'COMMAND', - GENERAL_REJECT: 'GENERAL_REJECT', + COMMAND: 'COMMAND', + GENERAL_REJECT: 'GENERAL_REJECT', RESPONSE_ACCEPT: 'RESPONSE_ACCEPT', - RESPONSE_REJECT: 'RESPONSE_REJECT' + RESPONSE_REJECT: 'RESPONSE_REJECT', } - subclasses = {} # Subclasses, by signal identifier and message type + # Subclasses, by signal identifier and message type + subclasses: Dict[int, Dict[int, Type[Message]]] = {} @staticmethod def message_type_name(message_type): return name_or_number(Message.MESSAGE_TYPE_NAMES, message_type) @staticmethod - def subclass(cls): + def subclass(subclass): # Infer the signal identifier and message subtype from the class name - name = cls.__name__ + name = subclass.__name__ if name == 'General_Reject': - cls.signal_identifier = 0 + subclass.signal_identifier = 0 signal_identifier_str = None message_type = Message.COMMAND elif name.endswith('_Command'): @@ -594,20 +656,23 @@ class Message: else: raise ValueError('invalid class name') - cls.message_type = message_type + subclass.message_type = message_type if signal_identifier_str is not None: for (name, signal_identifier) in AVDTP_SIGNAL_IDENTIFIERS.items(): if name.lower().endswith(signal_identifier_str.lower()): - cls.signal_identifier = signal_identifier + subclass.signal_identifier = signal_identifier break # Register the subclass - Message.subclasses.setdefault(cls.signal_identifier, {})[cls.message_type] = cls + Message.subclasses.setdefault(subclass.signal_identifier, {})[ + subclass.message_type + ] = subclass - return cls + return subclass - # Factory method to create a subclass based on the signal identifier and message type + # Factory method to create a subclass based on the signal identifier and message + # type @staticmethod def create(signal_identifier, message_type, payload): # Look for a registered subclass @@ -635,17 +700,26 @@ class Message: pass def __init__(self, payload=b''): - self.payload = payload + self.payload = payload def to_string(self, details): - base = f'{color(f"{name_or_number(AVDTP_SIGNAL_NAMES, self.signal_identifier)}_{Message.message_type_name(self.message_type)}", "yellow")}' + base = color( + f'{name_or_number(AVDTP_SIGNAL_NAMES, self.signal_identifier)}_' + f'{Message.message_type_name(self.message_type)}', + 'yellow', + ) + if details: - if type(details) is str: + if isinstance(details, str): return f'{base}: {details}' - else: - return base + ':\n' + '\n'.join([' ' + color(detail, 'cyan') for detail in details]) - else: - return base + + return ( + base + + ':\n' + + '\n'.join([' ' + color(detail, 'cyan') for detail in details]) + ) + + return base def __str__(self): return self.to_string(self.payload.hex()) @@ -661,8 +735,8 @@ class Simple_Command(Message): self.acp_seid = self.payload[0] >> 2 def __init__(self, seid): + super().__init__(payload=bytes([seid << 2])) self.acp_seid = seid - self.payload = bytes([seid << 2]) def __str__(self): return self.to_string([f'ACP SEID: {self.acp_seid}']) @@ -678,13 +752,11 @@ class Simple_Reject(Message): self.error_code = self.payload[0] def __init__(self, error_code): + super().__init__(payload=bytes([error_code])) self.error_code = error_code - self.payload = bytes([self.error_code]) def __str__(self): - details = [ - f'error_code: {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}' - ] + details = [f'error_code: {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}'] return self.to_string(details) @@ -707,21 +779,24 @@ class Discover_Response(Message): self.endpoints = [] endpoint_count = len(self.payload) // 2 for i in range(endpoint_count): - self.endpoints.append(EndPointInfo.from_bytes(self.payload[i * 2:(i + 1) * 2])) + self.endpoints.append( + EndPointInfo.from_bytes(self.payload[i * 2 : (i + 1) * 2]) + ) def __init__(self, endpoints): + super().__init__(payload=b''.join([bytes(endpoint) for endpoint in endpoints])) self.endpoints = endpoints - self.payload = b''.join([bytes(endpoint) for endpoint in endpoints]) def __str__(self): details = [] for endpoint in self.endpoints: details.extend( + # pylint: disable=line-too-long [ f'ACP SEID: {endpoint.seid}', f' in_use: {endpoint.in_use}', f' media_type: {name_or_number(AVDTP_MEDIA_TYPE_NAMES, endpoint.media_type)}', - f' tsep: {name_or_number(AVDTP_TSEP_NAMES, endpoint.tsep)}' + f' tsep: {name_or_number(AVDTP_TSEP_NAMES, endpoint.tsep)}', ] ) return self.to_string(details) @@ -746,8 +821,10 @@ class Get_Capabilities_Response(Message): self.capabilities = ServiceCapabilities.parse_capabilities(self.payload) def __init__(self, capabilities): + super().__init__( + payload=ServiceCapabilities.serialize_capabilities(capabilities) + ) self.capabilities = capabilities - self.payload = ServiceCapabilities.serialize_capabilities(capabilities) def __str__(self): details = [str(capability) for capability in self.capabilities] @@ -794,21 +871,23 @@ class Set_Configuration_Command(Message): ''' def init_from_payload(self): - self.acp_seid = self.payload[0] >> 2 - self.int_seid = self.payload[1] >> 2 + self.acp_seid = self.payload[0] >> 2 + self.int_seid = self.payload[1] >> 2 self.capabilities = ServiceCapabilities.parse_capabilities(self.payload[2:]) def __init__(self, acp_seid, int_seid, capabilities): - self.acp_seid = acp_seid - self.int_seid = int_seid + super().__init__( + payload=bytes([acp_seid << 2, int_seid << 2]) + + ServiceCapabilities.serialize_capabilities(capabilities) + ) + self.acp_seid = acp_seid + self.int_seid = int_seid self.capabilities = capabilities - self.payload = bytes([acp_seid << 2, int_seid << 2]) + ServiceCapabilities.serialize_capabilities(capabilities) def __str__(self): - details = [ - f'ACP SEID: {self.acp_seid}', - f'INT SEID: {self.int_seid}' - ] + [str(capability) for capability in self.capabilities] + details = [f'ACP SEID: {self.acp_seid}', f'INT SEID: {self.int_seid}'] + [ + str(capability) for capability in self.capabilities + ] return self.to_string(details) @@ -829,17 +908,23 @@ class Set_Configuration_Reject(Message): def init_from_payload(self): self.service_category = self.payload[0] - self.error_code = self.payload[1] + self.error_code = self.payload[1] def __init__(self, service_category, error_code): + super().__init__(payload=bytes([service_category, error_code])) self.service_category = service_category - self.error_code = error_code - self.payload = bytes([service_category, self.error_code]) + self.error_code = error_code def __str__(self): details = [ - f'service_category: {name_or_number(AVDTP_SERVICE_CATEGORY_NAMES, self.service_category)}', - f'error_code: {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}' + ( + 'service_category: ' + f'{name_or_number(AVDTP_SERVICE_CATEGORY_NAMES, self.service_category)}' + ), + ( + 'error_code: ' + f'{name_or_number(AVDTP_ERROR_NAMES, self.error_code)}' + ), ] return self.to_string(details) @@ -863,8 +948,10 @@ class Get_Configuration_Response(Message): self.capabilities = ServiceCapabilities.parse_capabilities(self.payload) def __init__(self, capabilities): + super().__init__( + payload=ServiceCapabilities.serialize_capabilities(capabilities) + ) self.capabilities = capabilities - self.payload = ServiceCapabilities.serialize_capabilities(capabilities) def __str__(self): details = [str(capability) for capability in self.capabilities] @@ -887,7 +974,8 @@ class Reconfigure_Command(Message): ''' def init_from_payload(self): - self.acp_seid = self.payload[0] >> 2 + # pylint: disable=attribute-defined-outside-init + self.acp_seid = self.payload[0] >> 2 self.capabilities = ServiceCapabilities.parse_capabilities(self.payload[1:]) def __str__(self): @@ -948,8 +1036,8 @@ class Start_Command(Message): self.acp_seids = [x >> 2 for x in self.payload] def __init__(self, seids): + super().__init__(payload=bytes([seid << 2 for seid in seids])) self.acp_seids = seids - self.payload = bytes([seid << 2 for seid in self.acp_seids]) def __str__(self): return self.to_string([f'ACP SEIDs: {self.acp_seids}']) @@ -971,18 +1059,18 @@ class Start_Reject(Message): ''' def init_from_payload(self): - self.acp_seid = self.payload[0] >> 2 + self.acp_seid = self.payload[0] >> 2 self.error_code = self.payload[1] def __init__(self, acp_seid, error_code): - self.acp_seid = acp_seid + super().__init__(payload=bytes([acp_seid << 2, error_code])) + self.acp_seid = acp_seid self.error_code = error_code - self.payload = bytes([self.acp_seid << 2, self.error_code]) def __str__(self): details = [ f'acp_seid: {self.acp_seid}', - f'error_code: {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}' + f'error_code: {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}', ] return self.to_string(details) @@ -1083,7 +1171,7 @@ class General_Reject(Message): ''' def to_string(self, details): - return f'{color(f"GENERAL_REJECT", "yellow")}' + return color('GENERAL_REJECT', 'yellow') # ----------------------------------------------------------------------------- @@ -1094,14 +1182,12 @@ class DelayReport_Command(Message): ''' def init_from_payload(self): + # pylint: disable=attribute-defined-outside-init self.acp_seid = self.payload[0] >> 2 - self.delay = (self.payload[1] << 8) | (self.payload[2]) + self.delay = (self.payload[1] << 8) | (self.payload[2]) def __str__(self): - return self.to_string([ - f'ACP_SEID: {self.acp_seid}', - f'delay: {self.delay}' - ]) + return self.to_string([f'ACP_SEID: {self.acp_seid}', f'delay: {self.delay}']) # ----------------------------------------------------------------------------- @@ -1122,16 +1208,16 @@ class DelayReport_Reject(Simple_Reject): # ----------------------------------------------------------------------------- class Protocol: - SINGLE_PACKET = 0 - START_PACKET = 1 + SINGLE_PACKET = 0 + START_PACKET = 1 CONTINUE_PACKET = 2 - END_PACKET = 3 + END_PACKET = 3 PACKET_TYPE_NAMES = { - SINGLE_PACKET: 'SINGLE_PACKET', - START_PACKET: 'START_PACKET', + SINGLE_PACKET: 'SINGLE_PACKET', + START_PACKET: 'START_PACKET', CONTINUE_PACKET: 'CONTINUE_PACKET', - END_PACKET: 'END_PACKET' + END_PACKET: 'END_PACKET', } @staticmethod @@ -1148,27 +1234,29 @@ class Protocol: return protocol def __init__(self, l2cap_channel, version=(1, 3)): - self.l2cap_channel = l2cap_channel - self.version = version - self.rtx_sig_timer = AVDTP_DEFAULT_RTX_SIG_TIMER - self.message_assembler = MessageAssembler(self.on_message) - self.transaction_results = [None] * 16 # Futures for up to 16 transactions + self.l2cap_channel = l2cap_channel + self.version = version + self.rtx_sig_timer = AVDTP_DEFAULT_RTX_SIG_TIMER + self.message_assembler = MessageAssembler(self.on_message) + self.transaction_results = [None] * 16 # Futures for up to 16 transactions self.transaction_semaphore = asyncio.Semaphore(16) - self.transaction_count = 0 - self.channel_acceptor = None - self.channel_connector = None - self.local_endpoints = [] # Local endpoints, with contiguous seid values - self.remote_endpoints = {} # Remote stream endpoints, by seid - self.streams = {} # Streams, by seid + self.transaction_count = 0 + self.channel_acceptor = None + self.channel_connector = None + self.local_endpoints = [] # Local endpoints, with contiguous seid values + self.remote_endpoints = {} # Remote stream endpoints, by seid + self.streams = {} # Streams, by seid # Register to receive PDUs from the channel l2cap_channel.sink = self.on_pdu l2cap_channel.on('open', self.on_l2cap_channel_open) def get_local_endpoint_by_seid(self, seid): - if seid > 0 and seid <= len(self.local_endpoints): + if 0 < seid <= len(self.local_endpoints): return self.local_endpoints[seid - 1] + return None + def add_source(self, codec_capabilities, packet_pump): seid = len(self.local_endpoints) + 1 source = LocalSource(self, seid, codec_capabilities, packet_pump) @@ -1205,7 +1293,9 @@ class Protocol: response = await self.send_command(Discover_Command()) for endpoint_entry in response.endpoints: - logger.debug(f'getting endpoint capabilities for endpoint {endpoint_entry.seid}') + logger.debug( + f'getting endpoint capabilities for endpoint {endpoint_entry.seid}' + ) get_capabilities_response = await self.get_capabilities(endpoint_entry.seid) endpoint = DiscoveredStreamEndPoint( self, @@ -1213,7 +1303,7 @@ class Protocol: endpoint_entry.media_type, endpoint_entry.tsep, endpoint_entry.in_use, - get_capabilities_response.capabilities + get_capabilities_response.capabilities, ) self.remote_endpoints[endpoint_entry.seid] = endpoint @@ -1221,23 +1311,41 @@ class Protocol: def find_remote_sink_by_codec(self, media_type, codec_type): for endpoint in self.remote_endpoints.values(): - if not endpoint.in_use and endpoint.media_type == media_type and endpoint.tsep == AVDTP_TSEP_SNK: + if ( + not endpoint.in_use + and endpoint.media_type == media_type + and endpoint.tsep == AVDTP_TSEP_SNK + ): has_media_transport = False has_codec = False for capabilities in endpoint.capabilities: - if capabilities.service_category == AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY: + if ( + capabilities.service_category + == AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY + ): has_media_transport = True - elif capabilities.service_category == AVDTP_MEDIA_CODEC_SERVICE_CATEGORY: - if capabilities.media_type == AVDTP_AUDIO_MEDIA_TYPE and capabilities.media_codec_type == codec_type: + elif ( + capabilities.service_category + == AVDTP_MEDIA_CODEC_SERVICE_CATEGORY + ): + if ( + capabilities.media_type == AVDTP_AUDIO_MEDIA_TYPE + and capabilities.media_codec_type == codec_type + ): has_codec = True if has_media_transport and has_codec: return endpoint + return None + def on_pdu(self, pdu): self.message_assembler.on_pdu(pdu) def on_message(self, transaction_label, message): - logger.debug(f'{color("<<< Received AVDTP message", "magenta")}: [{transaction_label}] {message}') + logger.debug( + f'{color("<<< Received AVDTP message", "magenta")}: ' + f'[{transaction_label}] {message}' + ) # Check that the identifier is not reserved if message.signal_identifier == 0: @@ -1245,20 +1353,30 @@ class Protocol: return # Check that the identifier is valid - if message.signal_identifier < 0 or message.signal_identifier > AVDTP_DELAYREPORT: + if ( + message.signal_identifier < 0 + or message.signal_identifier > AVDTP_DELAYREPORT + ): logger.warning('!!! invalid signal identifier') self.send_message(transaction_label, General_Reject()) if message.message_type == Message.COMMAND: # Command - handler_name = f'on_{AVDTP_SIGNAL_NAMES.get(message.signal_identifier,"").replace("AVDTP_","").lower()}_command' + signal_name = ( + AVDTP_SIGNAL_NAMES.get(message.signal_identifier, "") + .replace("AVDTP_", "") + .lower() + ) + handler_name = f'on_{signal_name}_command' handler = getattr(self, handler_name, None) if handler: try: response = handler(message) self.send_message(transaction_label, response) except Exception as error: - logger.warning(f'{color("!!! Exception in handler:", "red")} {error}') + logger.warning( + f'{color("!!! Exception in handler:", "red")} {error}' + ) else: logger.warning('unhandled command') else: @@ -1281,8 +1399,13 @@ class Protocol: logger.debug(color('<<< L2CAP channel open', 'magenta')) def send_message(self, transaction_label, message): - logger.debug(f'{color(">>> Sending AVDTP message", "magenta")}: [{transaction_label}] {message}') - max_fragment_size = self.l2cap_channel.mtu - 3 # Enough space for a 3-byte start packet header + logger.debug( + f'{color(">>> Sending AVDTP message", "magenta")}: ' + f'[{transaction_label}] {message}' + ) + max_fragment_size = ( + self.l2cap_channel.mtu - 3 + ) # Enough space for a 3-byte start packet header payload = message.payload if len(payload) + 2 <= self.l2cap_channel.mtu: # Fits in a single packet @@ -1292,13 +1415,19 @@ class Protocol: done = False while not done: - first_header_byte = transaction_label << 4 | packet_type << 2 | message.message_type + first_header_byte = ( + transaction_label << 4 | packet_type << 2 | message.message_type + ) if packet_type == self.SINGLE_PACKET: header = bytes([first_header_byte, message.signal_identifier]) elif packet_type == self.START_PACKET: - packet_count = (max_fragment_size - 1 + len(payload)) // max_fragment_size - header = bytes([first_header_byte, message.signal_identifier, packet_count]) + packet_count = ( + max_fragment_size - 1 + len(payload) + ) // max_fragment_size + header = bytes( + [first_header_byte, message.signal_identifier, packet_count] + ) else: header = bytes([first_header_byte]) @@ -1308,7 +1437,11 @@ class Protocol: # Prepare for the next packet payload = payload[max_fragment_size:] if payload: - packet_type = self.CONTINUE_PACKET if payload > max_fragment_size else self.END_PACKET + packet_type = ( + self.CONTINUE_PACKET + if payload > max_fragment_size + else self.END_PACKET + ) else: done = True @@ -1322,7 +1455,7 @@ class Protocol: response = await transaction_result # Check for errors - if response.message_type == Message.GENERAL_REJECT or response.message_type == Message.RESPONSE_REJECT: + if response.message_type in (Message.GENERAL_REJECT, Message.RESPONSE_REJECT): raise ProtocolError(response.error_code, 'avdtp') return response @@ -1340,16 +1473,18 @@ class Protocol: self.transaction_count += 1 return (transaction_label, transaction_result) - assert(False) # Should never reach this + assert False # Should never reach this async def get_capabilities(self, seid): if self.version > (1, 2): return await self.send_command(Get_All_Capabilities_Command(seid)) - else: - return await self.send_command(Get_Capabilities_Command(seid)) + + return await self.send_command(Get_Capabilities_Command(seid)) async def set_configuration(self, acp_seid, int_seid, capabilities): - return await self.send_command(Set_Configuration_Command(acp_seid, int_seid, capabilities)) + return await self.send_command( + Set_Configuration_Command(acp_seid, int_seid, capabilities) + ) async def get_configuration(self, seid): response = await self.send_command(Get_Configuration_Command(seid)) @@ -1370,7 +1505,7 @@ class Protocol: async def abort(self, seid): return await self.send_command(Abort_Command(seid)) - def on_discover_command(self, command): + def on_discover_command(self, _command): endpoint_infos = [ EndPointInfo(endpoint.seid, 0, endpoint.media_type, endpoint.tsep) for endpoint in self.local_endpoints @@ -1537,6 +1672,7 @@ class Listener(EventEmitter): server = Protocol(channel, self.version) self.set_server(channel.connection, server) self.emit('connection', server) + channel.on('open', on_channel_open) @@ -1562,8 +1698,7 @@ class Stream: raise InvalidStateError('current state is not IDLE') await self.remote_endpoint.set_configuration( - self.local_endpoint.seid, - self.local_endpoint.configuration + self.local_endpoint.seid, self.local_endpoint.configuration ) self.change_state(AVDTP_CONFIGURED_STATE) @@ -1608,7 +1743,7 @@ class Stream: self.change_state(AVDTP_OPEN_STATE) async def close(self): - if self.state not in {AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE}: + if self.state not in (AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE): raise InvalidStateError('current state is not OPEN or STREAMING') logger.debug('closing local endpoint') @@ -1637,9 +1772,14 @@ class Stream: return result self.change_state(AVDTP_CONFIGURED_STATE) + return None def on_get_configuration_command(self, configuration): - if self.state not in {AVDTP_CONFIGURED_STATE, AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE}: + if self.state not in ( + AVDTP_CONFIGURED_STATE, + AVDTP_OPEN_STATE, + AVDTP_STREAMING_STATE, + ): return Get_Configuration_Reject(AVDTP_BAD_STATE_ERROR) return self.local_endpoint.on_get_configuration_command(configuration) @@ -1652,6 +1792,8 @@ class Stream: if result is not None: return result + return None + def on_open_command(self): if self.state != AVDTP_CONFIGURED_STATE: return Open_Reject(AVDTP_BAD_STATE_ERROR) @@ -1664,6 +1806,7 @@ class Stream: self.protocol.channel_acceptor = self self.change_state(AVDTP_OPEN_STATE) + return None def on_start_command(self): if self.state != AVDTP_OPEN_STATE: @@ -1679,6 +1822,7 @@ class Stream: return result self.change_state(AVDTP_STREAMING_STATE) + return None def on_suspend_command(self): if self.state != AVDTP_STREAMING_STATE: @@ -1689,9 +1833,10 @@ class Stream: return result self.change_state(AVDTP_OPEN_STATE) + return None def on_close_command(self): - if self.state not in {AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE}: + if self.state not in (AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE): return Open_Reject(AVDTP_BAD_STATE_ERROR) result = self.local_endpoint.on_close_command() @@ -1707,6 +1852,8 @@ class Stream: # TODO: set a timer as we wait for the RTP channel to be closed pass + return None + def on_abort_command(self): if self.rtp_channel is None: # No need to wait @@ -1718,7 +1865,7 @@ class Stream: def on_l2cap_connection(self, channel): logger.debug(color('<<< stream channel connected', 'magenta')) self.rtp_channel = channel - channel.on('open', self.on_l2cap_channel_open) + channel.on('open', self.on_l2cap_channel_open) channel.on('close', self.on_l2cap_channel_close) # We don't need more channels @@ -1734,7 +1881,7 @@ class Stream: self.local_endpoint.in_use = 0 self.rtp_channel = None - if self.state in {AVDTP_CLOSING_STATE, AVDTP_ABORTING_STATE}: + if self.state in (AVDTP_CLOSING_STATE, AVDTP_ABORTING_STATE): self.change_state(AVDTP_IDLE_STATE) else: logger.warning('unexpected channel close while not CLOSING or ABORTING') @@ -1744,54 +1891,57 @@ class Stream: remote_endpoint must be a subclass of StreamEndPointProxy ''' - self.protocol = protocol - self.local_endpoint = local_endpoint + self.protocol = protocol + self.local_endpoint = local_endpoint self.remote_endpoint = remote_endpoint - self.rtp_channel = None - self.state = AVDTP_IDLE_STATE + self.rtp_channel = None + self.state = AVDTP_IDLE_STATE local_endpoint.stream = self local_endpoint.in_use = 1 def __str__(self): - return f'Stream({self.local_endpoint.seid} -> {self.remote_endpoint.seid} {self.state_name(self.state)})' + return ( + f'Stream({self.local_endpoint.seid} -> ' + f'{self.remote_endpoint.seid} {self.state_name(self.state)})' + ) # ----------------------------------------------------------------------------- class StreamEndPoint: def __init__(self, seid, media_type, tsep, in_use, capabilities): - self.seid = seid - self.media_type = media_type - self.tsep = tsep - self.in_use = in_use + self.seid = seid + self.media_type = media_type + self.tsep = tsep + self.in_use = in_use self.capabilities = capabilities def __str__(self): - return '\n'.join([ - 'SEP(', - f' seid={self.seid}', - f' media_type={name_or_number(AVDTP_MEDIA_TYPE_NAMES, self.media_type)}', - f' tsep={name_or_number(AVDTP_TSEP_NAMES, self.tsep)}', - f' in_use={self.in_use}', - ' capabilities=[', - '\n'.join([f' {x}' for x in self.capabilities]), - ' ]', - ')' - ]) + media_type = f'{name_or_number(AVDTP_MEDIA_TYPE_NAMES, self.media_type)}' + tsep = f'{name_or_number(AVDTP_TSEP_NAMES, self.tsep)}' + return '\n'.join( + [ + 'SEP(', + f' seid={self.seid}', + f' media_type={media_type}', + f' tsep={tsep}', + f' in_use={self.in_use}', + ' capabilities=[', + '\n'.join([f' {x}' for x in self.capabilities]), + ' ]', + ')', + ] + ) # ----------------------------------------------------------------------------- class StreamEndPointProxy: def __init__(self, protocol, seid): - self.seid = seid + self.seid = seid self.protocol = protocol async def set_configuration(self, int_seid, configuration): - return await self.protocol.set_configuration( - self.seid, - int_seid, - configuration - ) + return await self.protocol.set_configuration(self.seid, int_seid, configuration) async def open(self): return await self.protocol.open(self.seid) @@ -1818,11 +1968,13 @@ class DiscoveredStreamEndPoint(StreamEndPoint, StreamEndPointProxy): # ----------------------------------------------------------------------------- class LocalStreamEndPoint(StreamEndPoint): - def __init__(self, protocol, seid, media_type, tsep, capabilities, configuration=[]): + def __init__( + self, protocol, seid, media_type, tsep, capabilities, configuration=None + ): super().__init__(seid, media_type, tsep, 0, capabilities) - self.protocol = protocol - self.configuration = configuration - self.stream = None + self.protocol = protocol + self.configuration = configuration if configuration is not None else [] + self.stream = None async def start(self): pass @@ -1866,23 +2018,31 @@ class LocalSource(LocalStreamEndPoint, EventEmitter): def __init__(self, protocol, seid, codec_capabilities, packet_pump): capabilities = [ ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY), - codec_capabilities + codec_capabilities, ] - LocalStreamEndPoint.__init__(self, protocol, seid, codec_capabilities.media_type, AVDTP_TSEP_SRC, capabilities, capabilities) + LocalStreamEndPoint.__init__( + self, + protocol, + seid, + codec_capabilities.media_type, + AVDTP_TSEP_SRC, + capabilities, + capabilities, + ) EventEmitter.__init__(self) self.packet_pump = packet_pump async def start(self): if self.packet_pump: return await self.packet_pump.start(self.stream.rtp_channel) - else: - self.emit('start', self.stream.rtp_channel) + + self.emit('start', self.stream.rtp_channel) async def stop(self): if self.packet_pump: return await self.packet_pump.stop() - else: - self.emit('stop') + + self.emit('stop') def on_set_configuration_command(self, configuration): # For now, blindly accept the configuration @@ -1890,10 +2050,10 @@ class LocalSource(LocalStreamEndPoint, EventEmitter): self.configuration = configuration def on_start_command(self): - asyncio.get_running_loop().create_task(self.start()) + asyncio.create_task(self.start()) def on_suspend_command(self): - asyncio.get_running_loop().create_task(self.stop()) + asyncio.create_task(self.stop()) # ----------------------------------------------------------------------------- @@ -1901,9 +2061,16 @@ class LocalSink(LocalStreamEndPoint, EventEmitter): def __init__(self, protocol, seid, codec_capabilities): capabilities = [ ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY), - codec_capabilities + codec_capabilities, ] - LocalStreamEndPoint.__init__(self, protocol, seid, codec_capabilities.media_type, AVDTP_TSEP_SNK, capabilities) + LocalStreamEndPoint.__init__( + self, + protocol, + seid, + codec_capabilities.media_type, + AVDTP_TSEP_SNK, + capabilities, + ) EventEmitter.__init__(self) def on_set_configuration_command(self, configuration): @@ -1917,5 +2084,8 @@ class LocalSink(LocalStreamEndPoint, EventEmitter): def on_avdtp_packet(self, packet): rtp_packet = MediaPacket.from_bytes(packet) - logger.debug(f'{color("<<< RTP Packet:", "green")} {rtp_packet} {rtp_packet.payload[:16].hex()}') + logger.debug( + f'{color("<<< RTP Packet:", "green")} ' + f'{rtp_packet} {rtp_packet.payload[:16].hex()}' + ) self.emit('rtp_packet', rtp_packet) diff --git a/bumble/bridge.py b/bumble/bridge.py index 2b4cd94..ac3ba8a 100644 --- a/bumble/bridge.py +++ b/bumble/bridge.py @@ -30,10 +30,10 @@ logger = logging.getLogger(__name__) class HCI_Bridge: class Forwarder: def __init__(self, hci_sink, sender_hci_sink, packet_filter, trace): - self.hci_sink = hci_sink + self.hci_sink = hci_sink self.sender_hci_sink = sender_hci_sink - self.packet_filter = packet_filter - self.trace = trace + self.packet_filter = packet_filter + self.trace = trace def on_packet(self, packet): # Convert the packet bytes to an object @@ -61,15 +61,15 @@ class HCI_Bridge: hci_host_sink, hci_controller_source, hci_controller_sink, - host_to_controller_filter = None, - controller_to_host_filter = None + host_to_controller_filter=None, + controller_to_host_filter=None, ): tracer = PacketTracer(emit_message=logger.info) host_to_controller_forwarder = HCI_Bridge.Forwarder( hci_controller_sink, hci_host_sink, host_to_controller_filter, - lambda packet: tracer.trace(packet, 0) + lambda packet: tracer.trace(packet, 0), ) hci_host_source.set_packet_sink(host_to_controller_forwarder) @@ -77,6 +77,6 @@ class HCI_Bridge: hci_host_sink, hci_controller_sink, controller_to_host_filter, - lambda packet: tracer.trace(packet, 1) + lambda packet: tracer.trace(packet, 1), ) hci_controller_source.set_packet_sink(controller_to_host_forwarder) diff --git a/bumble/colors.py b/bumble/colors.py new file mode 100644 index 0000000..2813cfe --- /dev/null +++ b/bumble/colors.py @@ -0,0 +1,103 @@ +# Copyright (c) 2012 Giorgos Verigakis <verigak@gmail.com> +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +from functools import partial +from typing import List, Optional, Union + + +# ANSI color names. There is also a "default" +COLORS = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white') + +# ANSI style names +STYLES = ( + 'none', + 'bold', + 'faint', + 'italic', + 'underline', + 'blink', + 'blink2', + 'negative', + 'concealed', + 'crossed', +) + + +ColorSpec = Union[str, int] + + +def _join(*values: ColorSpec) -> str: + return ';'.join(str(v) for v in values) + + +def _color_code(spec: ColorSpec, base: int) -> str: + if isinstance(spec, str): + spec = spec.strip().lower() + + if spec == 'default': + return _join(base + 9) + elif spec in COLORS: + return _join(base + COLORS.index(spec)) + elif isinstance(spec, int) and 0 <= spec <= 255: + return _join(base + 8, 5, spec) + else: + raise ValueError('Invalid color spec "%s"' % spec) + + +def color( + s: str, + fg: Optional[ColorSpec] = None, + bg: Optional[ColorSpec] = None, + style: Optional[str] = None, +) -> str: + codes: List[ColorSpec] = [] + + if fg: + codes.append(_color_code(fg, 30)) + if bg: + codes.append(_color_code(bg, 40)) + if style: + for style_part in style.split('+'): + if style_part in STYLES: + codes.append(STYLES.index(style_part)) + else: + raise ValueError('Invalid style "%s"' % style_part) + + if codes: + return '\x1b[{0}m{1}\x1b[0m'.format(_join(*codes), s) + else: + return s + + +# Foreground color shortcuts +black = partial(color, fg='black') +red = partial(color, fg='red') +green = partial(color, fg='green') +yellow = partial(color, fg='yellow') +blue = partial(color, fg='blue') +magenta = partial(color, fg='magenta') +cyan = partial(color, fg='cyan') +white = partial(color, fg='white') + +# Style shortcuts +bold = partial(color, style='bold') +none = partial(color, style='none') +faint = partial(color, style='faint') +italic = partial(color, style='italic') +underline = partial(color, style='underline') +blink = partial(color, style='blink') +blink2 = partial(color, style='blink2') +negative = partial(color, style='negative') +concealed = partial(color, style='concealed') +crossed = partial(color, style='crossed') diff --git a/bumble/company_ids.py b/bumble/company_ids.py index c9c9d1a..1c40951 100644 --- a/bumble/company_ids.py +++ b/bumble/company_ids.py @@ -17,6 +17,7 @@ # the `generate_company_id_list.py` script # ----------------------------------------------------------------------------- +# pylint: disable=line-too-long COMPANY_IDENTIFIERS = { 0x0000: "Ericsson Technology Licensing", 0x0001: "Nokia Mobile Phones", @@ -196,28 +197,28 @@ COMPANY_IDENTIFIERS = { 0x00AF: "Cinetix", 0x00B0: "Passif Semiconductor Corp", 0x00B1: "Saris Cycling Group, Inc", - 0x00B2: "​Bekey A/S", - 0x00B3: "​Clarinox Technologies Pty. Ltd.", - 0x00B4: "​BDE Technology Co., Ltd.", + 0x00B2: "Bekey A/S", + 0x00B3: "Clarinox Technologies Pty. Ltd.", + 0x00B4: "BDE Technology Co., Ltd.", 0x00B5: "Swirl Networks", - 0x00B6: "​Meso international", - 0x00B7: "​TreLab Ltd", - 0x00B8: "​Qualcomm Innovation Center, Inc. (QuIC)", - 0x00B9: "​​Johnson Controls, Inc.", - 0x00BA: "​Starkey Laboratories Inc.", - 0x00BB: "​​S-Power Electronics Limited", - 0x00BC: "​​Ace Sensor Inc", - 0x00BD: "​​Aplix Corporation", - 0x00BE: "​​AAMP of America", - 0x00BF: "​​Stalmart Technology Limited", - 0x00C0: "​​AMICCOM Electronics Corporation", - 0x00C1: "​​Shenzhen Excelsecu Data Technology Co.,Ltd", - 0x00C2: "​​Geneq Inc.", - 0x00C3: "​​adidas AG", - 0x00C4: "​​LG Electronics​", - 0x00C5: "​Onset Computer Corporation", - 0x00C6: "​Selfly BV", - 0x00C7: "​Quuppa Oy.", + 0x00B6: "Meso international", + 0x00B7: "TreLab Ltd", + 0x00B8: "Qualcomm Innovation Center, Inc. (QuIC)", + 0x00B9: "Johnson Controls, Inc.", + 0x00BA: "Starkey Laboratories Inc.", + 0x00BB: "S-Power Electronics Limited", + 0x00BC: "Ace Sensor Inc", + 0x00BD: "Aplix Corporation", + 0x00BE: "AAMP of America", + 0x00BF: "Stalmart Technology Limited", + 0x00C0: "AMICCOM Electronics Corporation", + 0x00C1: "Shenzhen Excelsecu Data Technology Co.,Ltd", + 0x00C2: "Geneq Inc.", + 0x00C3: "adidas AG", + 0x00C4: "LG Electronics", + 0x00C5: "Onset Computer Corporation", + 0x00C6: "Selfly BV", + 0x00C7: "Quuppa Oy.", 0x00C8: "GeLo Inc", 0x00C9: "Evluma", 0x00CA: "MC10", @@ -249,10 +250,10 @@ COMPANY_IDENTIFIERS = { 0x00E4: "Laird Connectivity, Inc. formerly L.S. Research Inc.", 0x00E5: "Eden Software Consultants Ltd.", 0x00E6: "Freshtemp", - 0x00E7: "​KS Technologies", - 0x00E8: "​ACTS Technologies", - 0x00E9: "​Vtrack Systems", - 0x00EA: "​Nielsen-Kellerman Company", + 0x00E7: "KS Technologies", + 0x00E8: "ACTS Technologies", + 0x00E9: "Vtrack Systems", + 0x00EA: "Nielsen-Kellerman Company", 0x00EB: "Server Technology Inc.", 0x00EC: "BioResearch Associates", 0x00ED: "Jolly Logic, LLC", @@ -2704,5 +2705,5 @@ COMPANY_IDENTIFIERS = { 0x0A7C: "WAFERLOCK", 0x0A7D: "Freedman Electronics Pty Ltd", 0x0A7E: "Keba AG", - 0x0A7F: "Intuity Medical" -}
\ No newline at end of file + 0x0A7F: "Intuity Medical", +} diff --git a/bumble/controller.py b/bumble/controller.py index 35f7e92..cd7de3d 100644 --- a/bumble/controller.py +++ b/bumble/controller.py @@ -19,9 +19,47 @@ import logging import asyncio import itertools import random +import struct +from bumble.colors import color +from bumble.core import ( + BT_CENTRAL_ROLE, + BT_PERIPHERAL_ROLE, + BT_LE_TRANSPORT, + BT_BR_EDR_TRANSPORT, +) + +from bumble.hci import ( + HCI_ACL_DATA_PACKET, + HCI_COMMAND_DISALLOWED_ERROR, + HCI_COMMAND_PACKET, + HCI_COMMAND_STATUS_PENDING, + HCI_CONNECTION_TIMEOUT_ERROR, + HCI_CONTROLLER_BUSY_ERROR, + HCI_EVENT_PACKET, + HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR, + HCI_LE_1M_PHY, + HCI_SUCCESS, + HCI_UNKNOWN_HCI_COMMAND_ERROR, + HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR, + HCI_VERSION_BLUETOOTH_CORE_5_0, + Address, + HCI_AclDataPacket, + HCI_AclDataPacketAssembler, + HCI_Command_Complete_Event, + HCI_Command_Status_Event, + HCI_Connection_Complete_Event, + HCI_Connection_Request_Event, + HCI_Disconnection_Complete_Event, + HCI_Encryption_Change_Event, + HCI_LE_Advertising_Report_Event, + HCI_LE_Connection_Complete_Event, + HCI_LE_Read_Remote_Features_Complete_Event, + HCI_Number_Of_Completed_Packets_Event, + HCI_Packet, + HCI_Role_Change_Event, +) +from typing import Optional, Union, Dict -from .hci import * -from .l2cap import * # ----------------------------------------------------------------------------- # Logging @@ -38,72 +76,108 @@ class DataObject: # ----------------------------------------------------------------------------- class Connection: - def __init__(self, controller, handle, role, peer_address, link): - self.controller = controller - self.handle = handle - self.role = role + def __init__(self, controller, handle, role, peer_address, link, transport): + self.controller = controller + self.handle = handle + self.role = role self.peer_address = peer_address - self.link = link - self.assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu) + self.link = link + self.assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu) + self.transport = transport def on_hci_acl_data_packet(self, packet): self.assembler.feed_packet(packet) - self.controller.send_hci_packet(HCI_Number_Of_Completed_Packets_Event([(self.handle, 1)])) + self.controller.send_hci_packet( + HCI_Number_Of_Completed_Packets_Event([(self.handle, 1)]) + ) def on_acl_pdu(self, data): if self.link: - self.link.send_acl_data(self.controller.random_address, self.peer_address, data) + self.link.send_acl_data( + self.controller, self.peer_address, self.transport, data + ) # ----------------------------------------------------------------------------- class Controller: - def __init__(self, name, host_source = None, host_sink = None, link = None): - self.name = name + def __init__( + self, + name, + host_source=None, + host_sink=None, + link=None, + public_address: Optional[Union[bytes, str, Address]] = None, + ): + self.name = name self.hci_sink = None - self.link = link - - self.central_connections = {} # Connections where this controller is the central - self.peripheral_connections = {} # Connections where this controller is the peripheral - - self.hci_version = HCI_VERSION_BLUETOOTH_CORE_5_0 - self.hci_revision = 0 - self.lmp_version = HCI_VERSION_BLUETOOTH_CORE_5_0 - self.lmp_subversion = 0 - self.lmp_features = bytes.fromhex('0000000060000000') # BR/EDR Not Supported, LE Supported (Controller) - self.manufacturer_name = 0xFFFF - self.hc_le_data_packet_length = 27 + self.link = link + + self.central_connections: Dict[ + Address, Connection + ] = {} # Connections where this controller is the central + self.peripheral_connections: Dict[ + Address, Connection + ] = {} # Connections where this controller is the peripheral + self.classic_connections: Dict[ + Address, Connection + ] = {} # Connections in BR/EDR + + self.hci_version = HCI_VERSION_BLUETOOTH_CORE_5_0 + self.hci_revision = 0 + self.lmp_version = HCI_VERSION_BLUETOOTH_CORE_5_0 + self.lmp_subversion = 0 + self.lmp_features = bytes.fromhex( + '0000000060000000' + ) # BR/EDR Not Supported, LE Supported (Controller) + self.manufacturer_name = 0xFFFF + self.hc_le_data_packet_length = 27 self.hc_total_num_le_data_packets = 64 - self.supported_commands = bytes.fromhex('2000800000c000000000e40000002822000000000000040000f7ffff7f00000030f0f9ff01008004000000000000000000000000000000000000000000000000') - self.le_features = bytes.fromhex('ff49010000000000') - self.le_states = bytes.fromhex('ffff3fffff030000') - self.avertising_channel_tx_power = 0 - self.white_list_size = 8 - self.resolving_list_size = 8 - self.supported_max_tx_octets = 27 - self.supported_max_tx_time = 10000 # microseconds - self.supported_max_rx_octets = 27 - self.supported_max_rx_time = 10000 # microseconds - self.suggested_max_tx_octets = 27 - self.suggested_max_tx_time = 0x0148 # microseconds - self.default_phy = bytes([0, 0, 0]) - self.le_scan_type = 0 - self.le_scan_interval = 0x10 - self.le_scan_window = 0x10 - self.le_scan_enable = 0 - self.le_scan_own_address_type = Address.RANDOM_DEVICE_ADDRESS - self.le_scanning_filter_policy = 0 - self.le_scan_response_data = None - self.le_address_resolution = False - self.le_rpa_timeout = 0 - self.sync_flow_control = False - self.local_name = 'Bumble' - - self.advertising_interval = 2000 # Fixed for now - self.advertising_data = None + self.event_mask = 0 + self.event_mask_page_2 = 0 + self.supported_commands = bytes.fromhex( + '2000800000c000000000e40000002822000000000000040000f7ffff7f000000' + '30f0f9ff01008004000000000000000000000000000000000000000000000000' + ) + self.le_event_mask = 0 + self.advertising_parameters = None + self.le_features = bytes.fromhex('ff49010000000000') + self.le_states = bytes.fromhex('ffff3fffff030000') + self.advertising_channel_tx_power = 0 + self.filter_accept_list_size = 8 + self.filter_duplicates = False + self.resolving_list_size = 8 + self.supported_max_tx_octets = 27 + self.supported_max_tx_time = 10000 # microseconds + self.supported_max_rx_octets = 27 + self.supported_max_rx_time = 10000 # microseconds + self.suggested_max_tx_octets = 27 + self.suggested_max_tx_time = 0x0148 # microseconds + self.default_phy = bytes([0, 0, 0]) + self.le_scan_type = 0 + self.le_scan_interval = 0x10 + self.le_scan_window = 0x10 + self.le_scan_enable = 0 + self.le_scan_own_address_type = Address.RANDOM_DEVICE_ADDRESS + self.le_scanning_filter_policy = 0 + self.le_scan_response_data = None + self.le_address_resolution = False + self.le_rpa_timeout = 0 + self.sync_flow_control = False + self.local_name = 'Bumble' + + self.advertising_interval = 2000 # Fixed for now + self.advertising_data = None self.advertising_timer_handle = None self._random_address = Address('00:00:00:00:00:00') - self._public_address = None + if isinstance(public_address, Address): + self._public_address = public_address + elif public_address is not None: + self._public_address = Address( + public_address, Address.PUBLIC_DEVICE_ADDRESS + ) + else: + self._public_address = Address('00:00:00:00:00:00') # Set the source and sink interfaces if host_source: @@ -121,7 +195,8 @@ class Controller: @host.setter def host(self, host): ''' - Sets the host (sink) for this controller, and set this controller as the controller (sink) for the host + Sets the host (sink) for this controller, and set this controller as the + controller (sink) for the host ''' self.set_packet_sink(host) if host: @@ -139,7 +214,7 @@ class Controller: @public_address.setter def public_address(self, address): - if type(address) is str: + if isinstance(address, str): address = Address(address) self._public_address = address @@ -149,7 +224,7 @@ class Controller: @random_address.setter def random_address(self, address): - if type(address) is str: + if isinstance(address, str): address = Address(address) self._random_address = address logger.debug(f'new random address: {address}') @@ -162,7 +237,10 @@ class Controller: self.on_hci_packet(HCI_Packet.from_bytes(packet)) def on_hci_packet(self, packet): - logger.debug(f'{color("<<<", "blue")} [{self.name}] {color("HOST -> CONTROLLER", "blue")}: {packet}') + logger.debug( + f'{color("<<<", "blue")} [{self.name}] ' + f'{color("HOST -> CONTROLLER", "blue")}: {packet}' + ) # If the packet is a command, invoke the handler for this packet if packet.hci_packet_type == HCI_COMMAND_PACKET: @@ -178,28 +256,35 @@ class Controller: handler_name = f'on_{command.name.lower()}' handler = getattr(self, handler_name, self.on_hci_command) result = handler(command) - if type(result) is bytes: - self.send_hci_packet(HCI_Command_Complete_Event( - num_hci_command_packets = 1, - command_opcode = command.op_code, - return_parameters = result - )) - - def on_hci_event_packet(self, event): + if isinstance(result, bytes): + self.send_hci_packet( + HCI_Command_Complete_Event( + num_hci_command_packets=1, + command_opcode=command.op_code, + return_parameters=result, + ) + ) + + def on_hci_event_packet(self, _event): logger.warning('!!! unexpected event packet') def on_hci_acl_data_packet(self, packet): # Look for the connection to which this data belongs connection = self.find_connection_by_handle(packet.connection_handle) if connection is None: - logger.warning(f'!!! no connection for handle 0x{packet.connection_handle:04X}') + logger.warning( + f'!!! no connection for handle 0x{packet.connection_handle:04X}' + ) return # Pass the packet to the connection connection.on_hci_acl_data_packet(packet) def send_hci_packet(self, packet): - logger.debug(f'{color(">>>", "green")} [{self.name}] {color("CONTROLLER -> HOST", "green")}: {packet}') + logger.debug( + f'{color(">>>", "green")} [{self.name}] ' + f'{color("CONTROLLER -> HOST", "green")}: {packet}' + ) if self.host: self.host.on_packet(packet.to_bytes()) @@ -216,7 +301,8 @@ class Controller: max_handle = 0 for connection in itertools.chain( self.central_connections.values(), - self.peripheral_connections.values() + self.peripheral_connections.values(), + self.classic_connections.values(), ): max_handle = max(max_handle, connection.handle) if connection.handle == handle: @@ -224,13 +310,19 @@ class Controller: handle = max_handle + 1 return handle - def find_connection_by_address(self, address): - return self.central_connections.get(address) or self.peripheral_connections.get(address) + def find_le_connection_by_address(self, address): + return self.central_connections.get(address) or self.peripheral_connections.get( + address + ) + + def find_classic_connection_by_address(self, address): + return self.classic_connections.get(address) def find_connection_by_handle(self, handle): for connection in itertools.chain( self.central_connections.values(), - self.peripheral_connections.values() + self.peripheral_connections.values(), + self.classic_connections.values(), ): if connection.handle == handle: return connection @@ -242,6 +334,12 @@ class Controller: return connection return None + def find_classic_connection_by_handle(self, handle): + for connection in self.classic_connections.values(): + if connection.handle == handle: + return connection + return None + def on_link_central_connected(self, central_address): ''' Called when an incoming connection occurs from a central on the link @@ -253,22 +351,31 @@ class Controller: connection = self.peripheral_connections.get(peer_address) if connection is None: connection_handle = self.allocate_connection_handle() - connection = Connection(self, connection_handle, BT_PERIPHERAL_ROLE, peer_address, self.link) + connection = Connection( + self, + connection_handle, + BT_PERIPHERAL_ROLE, + peer_address, + self.link, + BT_LE_TRANSPORT, + ) self.peripheral_connections[peer_address] = connection logger.debug(f'New PERIPHERAL connection handle: 0x{connection_handle:04X}') # Then say that the connection has completed - self.send_hci_packet(HCI_LE_Connection_Complete_Event( - status = HCI_SUCCESS, - connection_handle = connection.handle, - role = connection.role, - peer_address_type = peer_address_type, - peer_address = peer_address, - conn_interval = 10, # FIXME - conn_latency = 0, # FIXME - supervision_timeout = 10, # FIXME - master_clock_accuracy = 7 # FIXME - )) + self.send_hci_packet( + HCI_LE_Connection_Complete_Event( + status=HCI_SUCCESS, + connection_handle=connection.handle, + role=connection.role, + peer_address_type=peer_address_type, + peer_address=peer_address, + connection_interval=10, # FIXME + peripheral_latency=0, # FIXME + supervision_timeout=10, # FIXME + central_clock_accuracy=7, # FIXME + ) + ) def on_link_central_disconnected(self, peer_address, reason): ''' @@ -277,18 +384,22 @@ class Controller: # Send a disconnection complete event if connection := self.peripheral_connections.get(peer_address): - self.send_hci_packet(HCI_Disconnection_Complete_Event( - status = HCI_SUCCESS, - connection_handle = connection.handle, - reason = reason - )) + self.send_hci_packet( + HCI_Disconnection_Complete_Event( + status=HCI_SUCCESS, + connection_handle=connection.handle, + reason=reason, + ) + ) # Remove the connection del self.peripheral_connections[peer_address] else: - logger.warn(f'!!! No peripheral connection found for {peer_address}') + logger.warning(f'!!! No peripheral connection found for {peer_address}') - def on_link_peripheral_connection_complete(self, le_create_connection_command, status): + def on_link_peripheral_connection_complete( + self, le_create_connection_command, status + ): ''' Called by the link when a connection has been made or has failed to be made ''' @@ -304,25 +415,31 @@ class Controller: connection_handle, BT_CENTRAL_ROLE, peer_address, - self.link + self.link, + BT_LE_TRANSPORT, ) self.central_connections[peer_address] = connection - logger.debug(f'New CENTRAL connection handle: 0x{connection_handle:04X}') + logger.debug( + f'New CENTRAL connection handle: 0x{connection_handle:04X}' + ) else: connection = None # Say that the connection has completed - self.send_hci_packet(HCI_LE_Connection_Complete_Event( - status = status, - connection_handle = connection.handle if connection else 0, - role = BT_CENTRAL_ROLE, - peer_address_type = le_create_connection_command.peer_address_type, - peer_address = le_create_connection_command.peer_address, - conn_interval = le_create_connection_command.conn_interval_min, - conn_latency = le_create_connection_command.conn_latency, - supervision_timeout = le_create_connection_command.supervision_timeout, - master_clock_accuracy = 0 - )) + self.send_hci_packet( + # pylint: disable=line-too-long + HCI_LE_Connection_Complete_Event( + status=status, + connection_handle=connection.handle if connection else 0, + role=BT_CENTRAL_ROLE, + peer_address_type=le_create_connection_command.peer_address_type, + peer_address=le_create_connection_command.peer_address, + connection_interval=le_create_connection_command.connection_interval_min, + peripheral_latency=le_create_connection_command.max_latency, + supervision_timeout=le_create_connection_command.supervision_timeout, + central_clock_accuracy=0, + ) + ) def on_link_peripheral_disconnection_complete(self, disconnection_command, status): ''' @@ -330,14 +447,18 @@ class Controller: ''' # Send a disconnection complete event - self.send_hci_packet(HCI_Disconnection_Complete_Event( - status = status, - connection_handle = disconnection_command.connection_handle, - reason = disconnection_command.reason - )) + self.send_hci_packet( + HCI_Disconnection_Complete_Event( + status=status, + connection_handle=disconnection_command.connection_handle, + reason=disconnection_command.reason, + ) + ) # Remove the connection - if connection := self.find_central_connection_by_handle(disconnection_command.connection_handle): + if connection := self.find_central_connection_by_handle( + disconnection_command.connection_handle + ): logger.debug(f'CENTRAL Connection removed: {connection}') del self.central_connections[connection.peer_address] @@ -348,31 +469,34 @@ class Controller: # Send a disconnection complete event if connection := self.central_connections.get(peer_address): - self.send_hci_packet(HCI_Disconnection_Complete_Event( - status = HCI_SUCCESS, - connection_handle = connection.handle, - reason = HCI_CONNECTION_TIMEOUT_ERROR - )) + self.send_hci_packet( + HCI_Disconnection_Complete_Event( + status=HCI_SUCCESS, + connection_handle=connection.handle, + reason=HCI_CONNECTION_TIMEOUT_ERROR, + ) + ) # Remove the connection del self.central_connections[peer_address] else: - logger.warn(f'!!! No central connection found for {peer_address}') + logger.warning(f'!!! No central connection found for {peer_address}') - def on_link_encrypted(self, peer_address, rand, ediv, ltk): + def on_link_encrypted(self, peer_address, _rand, _ediv, _ltk): # For now, just setup the encryption without asking the host - if connection := self.find_connection_by_address(peer_address): + if connection := self.find_le_connection_by_address(peer_address): self.send_hci_packet( HCI_Encryption_Change_Event( - status = 0, - connection_handle = connection.handle, - encryption_enabled = 1 + status=0, connection_handle=connection.handle, encryption_enabled=1 ) ) - def on_link_acl_data(self, sender_address, data): + def on_link_acl_data(self, sender_address, transport, data): # Look for the connection to which this data belongs - connection = self.find_connection_by_address(sender_address) + if transport == BT_LE_TRANSPORT: + connection = self.find_le_connection_by_address(sender_address) + else: + connection = self.find_classic_connection_by_address(sender_address) if connection is None: logger.warning(f'!!! no connection for {sender_address}') return @@ -388,40 +512,125 @@ class Controller: return # Send a scan report - report = HCI_Object( - HCI_LE_Advertising_Report_Event.REPORT_FIELDS, - event_type = HCI_LE_Advertising_Report_Event.ADV_IND, - address_type = sender_address.address_type, - address = sender_address, - data = data, - rssi = -50 + report = HCI_LE_Advertising_Report_Event.Report( + HCI_LE_Advertising_Report_Event.Report.FIELDS, + event_type=HCI_LE_Advertising_Report_Event.ADV_IND, + address_type=sender_address.address_type, + address=sender_address, + data=data, + rssi=-50, ) self.send_hci_packet(HCI_LE_Advertising_Report_Event([report])) # Simulate a scan response - report = HCI_Object( - HCI_LE_Advertising_Report_Event.REPORT_FIELDS, - event_type = HCI_LE_Advertising_Report_Event.SCAN_RSP, - address_type = sender_address.address_type, - address = sender_address, - data = data, - rssi = -50 + report = HCI_LE_Advertising_Report_Event.Report( + HCI_LE_Advertising_Report_Event.Report.FIELDS, + event_type=HCI_LE_Advertising_Report_Event.SCAN_RSP, + address_type=sender_address.address_type, + address=sender_address, + data=data, + rssi=-50, ) self.send_hci_packet(HCI_LE_Advertising_Report_Event([report])) ############################################################ + # Classic link connections + ############################################################ + + def on_classic_connection_request(self, peer_address, link_type): + self.send_hci_packet( + HCI_Connection_Request_Event( + bd_addr=peer_address, + class_of_device=0, + link_type=link_type, + ) + ) + + def on_classic_connection_complete(self, peer_address, status): + if status == HCI_SUCCESS: + # Allocate (or reuse) a connection handle + peer_address = peer_address + connection = self.classic_connections.get(peer_address) + if connection is None: + connection_handle = self.allocate_connection_handle() + connection = Connection( + controller=self, + handle=connection_handle, + # Role doesn't matter in Classic because they are managed by HCI_Role_Change and HCI_Role_Discovery + role=BT_CENTRAL_ROLE, + peer_address=peer_address, + link=self.link, + transport=BT_BR_EDR_TRANSPORT, + ) + self.classic_connections[peer_address] = connection + logger.debug( + f'New CLASSIC connection handle: 0x{connection_handle:04X}' + ) + else: + connection_handle = connection.handle + self.send_hci_packet( + HCI_Connection_Complete_Event( + status=status, + connection_handle=connection_handle, + bd_addr=peer_address, + encryption_enabled=False, + link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE, + ) + ) + else: + connection = None + self.send_hci_packet( + HCI_Connection_Complete_Event( + status=status, + connection_handle=0, + bd_addr=peer_address, + encryption_enabled=False, + link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE, + ) + ) + + def on_classic_disconnected(self, peer_address, reason): + # Send a disconnection complete event + if connection := self.classic_connections.get(peer_address): + self.send_hci_packet( + HCI_Disconnection_Complete_Event( + status=HCI_SUCCESS, + connection_handle=connection.handle, + reason=reason, + ) + ) + + # Remove the connection + del self.classic_connections[peer_address] + else: + logger.warning(f'!!! No classic connection found for {peer_address}') + + def on_classic_role_change(self, peer_address, new_role): + self.send_hci_packet( + HCI_Role_Change_Event( + status=HCI_SUCCESS, + bd_addr=peer_address, + new_role=new_role, + ) + ) + + ############################################################ # Advertising support ############################################################ def on_advertising_timer_fired(self): self.send_advertising_data() - self.advertising_timer_handle = asyncio.get_running_loop().call_later(self.advertising_interval / 1000.0, self.on_advertising_timer_fired) + self.advertising_timer_handle = asyncio.get_running_loop().call_later( + self.advertising_interval / 1000.0, self.on_advertising_timer_fired + ) def start_advertising(self): # Stop any ongoing advertising before we start again self.stop_advertising() # Advertise now - self.advertising_timer_handle = asyncio.get_running_loop().call_soon(self.on_advertising_timer_fired) + self.advertising_timer_handle = asyncio.get_running_loop().call_soon( + self.on_advertising_timer_fired + ) def stop_advertising(self): if self.advertising_timer_handle is not None: @@ -448,29 +657,97 @@ class Controller: See Bluetooth spec Vol 2, Part E - 7.1.5 Create Connection command ''' - # TODO: classic mode not supported yet + if self.link is None: + return + logger.debug(f'Connection request to {command.bd_addr}') + + # Check that we don't already have a pending connection + if self.link.get_pending_connection(): + self.send_hci_packet( + HCI_Command_Status_Event( + status=HCI_CONTROLLER_BUSY_ERROR, + num_hci_command_packets=1, + command_opcode=command.op_code, + ) + ) + return + + self.link.classic_connect(self, command.bd_addr) + + # Say that the connection is pending + self.send_hci_packet( + HCI_Command_Status_Event( + status=HCI_COMMAND_STATUS_PENDING, + num_hci_command_packets=1, + command_opcode=command.op_code, + ) + ) def on_hci_disconnect_command(self, command): ''' See Bluetooth spec Vol 2, Part E - 7.1.6 Disconnect Command ''' # First, say that the disconnection is pending - self.send_hci_packet(HCI_Command_Status_Event( - status = HCI_COMMAND_STATUS_PENDING, - num_hci_command_packets = 1, - command_opcode = command.op_code - )) + self.send_hci_packet( + HCI_Command_Status_Event( + status=HCI_COMMAND_STATUS_PENDING, + num_hci_command_packets=1, + command_opcode=command.op_code, + ) + ) # Notify the link of the disconnection - if not (connection := self.find_central_connection_by_handle(command.connection_handle)): - logger.warn('connection not found') + handle = command.connection_handle + if connection := self.find_central_connection_by_handle(handle): + if self.link: + self.link.disconnect( + self.random_address, connection.peer_address, command + ) + else: + # Remove the connection + del self.central_connections[connection.peer_address] + elif connection := self.find_classic_connection_by_handle(handle): + if self.link: + self.link.classic_disconnect( + self, + connection.peer_address, + HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR, + ) + else: + # Remove the connection + del self.classic_connections[connection.peer_address] + + def on_hci_accept_connection_request_command(self, command): + ''' + See Bluetooth spec Vol 2, Part E - 7.1.8 Accept Connection Request command + ''' + + if self.link is None: return + self.send_hci_packet( + HCI_Command_Status_Event( + status=HCI_SUCCESS, + num_hci_command_packets=1, + command_opcode=command.op_code, + ) + ) + self.link.classic_accept_connection(self, command.bd_addr, command.role) - if self.link: - self.link.disconnect(self.random_address, connection.peer_address, command) - else: - # Remove the connection - del self.central_connections[connection.peer_address] + def on_hci_switch_role_command(self, command): + ''' + See Bluetooth spec Vol 2, Part E - 7.2.8 Switch Role command + ''' + + if self.link is None: + return + self.send_hci_packet( + HCI_Command_Status_Event( + status=HCI_SUCCESS, + num_hci_command_packets=1, + command_opcode=command.op_code, + ) + ) + self.link.classic_switch_role(self, command.bd_addr, command.role) def on_hci_set_event_mask_command(self, command): ''' @@ -479,7 +756,7 @@ class Controller: self.event_mask = command.event_mask return bytes([HCI_SUCCESS]) - def on_hci_reset_command(self, command): + def on_hci_reset_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.3.2 Reset Command ''' @@ -501,7 +778,7 @@ class Controller: pass return bytes([HCI_SUCCESS]) - def on_hci_read_local_name_command(self, command): + def on_hci_read_local_name_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.3.12 Read Local Name Command ''' @@ -511,21 +788,22 @@ class Controller: return bytes([HCI_SUCCESS]) + local_name - def on_hci_read_class_of_device_command(self, command): + def on_hci_read_class_of_device_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.3.25 Read Class of Device Command ''' return bytes([HCI_SUCCESS, 0, 0, 0]) - def on_hci_write_class_of_device_command(self, command): + def on_hci_write_class_of_device_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.3.26 Write Class of Device Command ''' return bytes([HCI_SUCCESS]) - def on_hci_read_synchronous_flow_control_enable_command(self, command): + def on_hci_read_synchronous_flow_control_enable_command(self, _command): ''' - See Bluetooth spec Vol 2, Part E - 7.3.36 Read Synchronous Flow Control Enable Command + See Bluetooth spec Vol 2, Part E - 7.3.36 Read Synchronous Flow Control Enable + Command ''' if self.sync_flow_control: ret = 1 @@ -535,7 +813,8 @@ class Controller: def on_hci_write_synchronous_flow_control_enable_command(self, command): ''' - See Bluetooth spec Vol 2, Part E - 7.3.37 Write Synchronous Flow Control Enable Command + See Bluetooth spec Vol 2, Part E - 7.3.37 Write Synchronous Flow Control Enable + Command ''' ret = HCI_SUCCESS if command.synchronous_flow_control_enable == 1: @@ -546,7 +825,13 @@ class Controller: ret = HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR return bytes([ret]) - def on_hci_write_simple_pairing_mode_command(self, command): + def on_hci_write_extended_inquiry_response_command(self, _command): + ''' + See Bluetooth spec Vol 2, Part E - 7.3.59 Write Simple Pairing Mode Command + ''' + return bytes([HCI_SUCCESS]) + + def on_hci_write_simple_pairing_mode_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.3.59 Write Simple Pairing Mode Command ''' @@ -559,13 +844,13 @@ class Controller: self.event_mask_page_2 = command.event_mask_page_2 return bytes([HCI_SUCCESS]) - def on_hci_read_le_host_support_command(self, command): + def on_hci_read_le_host_support_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.3.78 Write LE Host Support Command ''' return bytes([HCI_SUCCESS, 1, 0]) - def on_hci_write_le_host_support_command(self, command): + def on_hci_write_le_host_support_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.3.79 Write LE Host Support Command ''' @@ -574,40 +859,47 @@ class Controller: def on_hci_write_authenticated_payload_timeout_command(self, command): ''' - See Bluetooth spec Vol 2, Part E - 7.3.94 Write Authenticated Payload Timeout Command + See Bluetooth spec Vol 2, Part E - 7.3.94 Write Authenticated Payload Timeout + Command ''' # TODO return struct.pack('<BH', HCI_SUCCESS, command.connection_handle) - def on_hci_read_local_version_information_command(self, command): + def on_hci_read_local_version_information_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.4.1 Read Local Version Information Command ''' - return struct.pack('<BBHBHH', - HCI_SUCCESS, - self.hci_version, - self.hci_revision, - self.lmp_version, - self.manufacturer_name, - self.lmp_subversion) + return struct.pack( + '<BBHBHH', + HCI_SUCCESS, + self.hci_version, + self.hci_revision, + self.lmp_version, + self.manufacturer_name, + self.lmp_subversion, + ) - def on_hci_read_local_supported_commands_command(self, command): + def on_hci_read_local_supported_commands_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.4.2 Read Local Supported Commands Command ''' return bytes([HCI_SUCCESS]) + self.supported_commands - def on_hci_read_local_supported_features_command(self, command): + def on_hci_read_local_supported_features_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.4.3 Read Local Supported Features Command ''' return bytes([HCI_SUCCESS]) + self.lmp_features - def on_hci_read_bd_addr_command(self, command): + def on_hci_read_bd_addr_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.4.6 Read BD_ADDR Command ''' - bd_addr = self._public_address.to_bytes() if self._public_address is not None else bytes(6) + bd_addr = ( + self._public_address.to_bytes() + if self._public_address is not None + else bytes(6) + ) return bytes([HCI_SUCCESS]) + bd_addr def on_hci_le_set_event_mask_command(self, command): @@ -617,18 +909,21 @@ class Controller: self.le_event_mask = command.le_event_mask return bytes([HCI_SUCCESS]) - def on_hci_le_read_buffer_size_command(self, command): + def on_hci_le_read_buffer_size_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.8.2 LE Read Buffer Size Command ''' - return struct.pack('<BHB', - HCI_SUCCESS, - self.hc_le_data_packet_length, - self.hc_total_num_le_data_packets) + return struct.pack( + '<BHB', + HCI_SUCCESS, + self.hc_le_data_packet_length, + self.hc_total_num_le_data_packets, + ) - def on_hci_le_read_local_supported_features_command(self, command): + def on_hci_le_read_local_supported_features_command(self, _command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.3 LE Read Local Supported Features Command + See Bluetooth spec Vol 2, Part E - 7.8.3 LE Read Local Supported Features + Command ''' return bytes([HCI_SUCCESS]) + self.le_features @@ -646,11 +941,12 @@ class Controller: self.advertising_parameters = command return bytes([HCI_SUCCESS]) - def on_hci_le_read_advertising_channel_tx_power_command(self, command): + def on_hci_le_read_advertising_physical_channel_tx_power_command(self, _command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.6 LE Read Advertising Channel Tx Power Command + See Bluetooth spec Vol 2, Part E - 7.8.6 LE Read Advertising Physical Channel + Tx Power Command ''' - return bytes([HCI_SUCCESS, self.avertising_channel_tx_power]) + return bytes([HCI_SUCCESS, self.advertising_channel_tx_power]) def on_hci_le_set_advertising_data_command(self, command): ''' @@ -681,10 +977,10 @@ class Controller: ''' See Bluetooth spec Vol 2, Part E - 7.8.10 LE Set Scan Parameters Command ''' - self.le_scan_type = command.le_scan_type - self.le_scan_interval = command.le_scan_interval - self.le_scan_window = command.le_scan_window - self.le_scan_own_address_type = command.own_address_type + self.le_scan_type = command.le_scan_type + self.le_scan_interval = command.le_scan_interval + self.le_scan_window = command.le_scan_window + self.le_scan_own_address_type = command.own_address_type self.le_scanning_filter_policy = command.scanning_filter_policy return bytes([HCI_SUCCESS]) @@ -692,7 +988,7 @@ class Controller: ''' See Bluetooth spec Vol 2, Part E - 7.8.11 LE Set Scan Enable Command ''' - self.le_scan_enable = command.le_scan_enable + self.le_scan_enable = command.le_scan_enable self.filter_duplicates = command.filter_duplicates return bytes([HCI_SUCCESS]) @@ -708,50 +1004,57 @@ class Controller: # Check that we don't already have a pending connection if self.link.get_pending_connection(): - self.send_hci_packet(HCI_Command_Status_Event( - status = HCI_COMMAND_DISALLOWED_ERROR, - num_hci_command_packets = 1, - command_opcode = command.op_code - )) + self.send_hci_packet( + HCI_Command_Status_Event( + status=HCI_COMMAND_DISALLOWED_ERROR, + num_hci_command_packets=1, + command_opcode=command.op_code, + ) + ) return # Initiate the connection self.link.connect(self.random_address, command) # Say that the connection is pending - self.send_hci_packet(HCI_Command_Status_Event( - status = HCI_COMMAND_STATUS_PENDING, - num_hci_command_packets = 1, - command_opcode = command.op_code - )) + self.send_hci_packet( + HCI_Command_Status_Event( + status=HCI_COMMAND_STATUS_PENDING, + num_hci_command_packets=1, + command_opcode=command.op_code, + ) + ) - def on_hci_le_create_connection_cancel_command(self, command): + def on_hci_le_create_connection_cancel_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.8.13 LE Create Connection Cancel Command ''' return bytes([HCI_SUCCESS]) - def on_hci_le_read_white_list_size_command(self, command): + def on_hci_le_read_filter_accept_list_size_command(self, _command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.14 LE Read White List Size Command + See Bluetooth spec Vol 2, Part E - 7.8.14 LE Read Filter Accept List Size + Command ''' - return bytes([HCI_SUCCESS, self.white_list_size]) + return bytes([HCI_SUCCESS, self.filter_accept_list_size]) - def on_hci_le_clear_white_list_command(self, command): + def on_hci_le_clear_filter_accept_list_command(self, _command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.15 LE Clear White List Command + See Bluetooth spec Vol 2, Part E - 7.8.15 LE Clear Filter Accept List Command ''' return bytes([HCI_SUCCESS]) - def on_hci_le_add_device_to_white_list_command(self, command): + def on_hci_le_add_device_to_filter_accept_list_command(self, _command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.16 LE Add Device To White List Command + See Bluetooth spec Vol 2, Part E - 7.8.16 LE Add Device To Filter Accept List + Command ''' return bytes([HCI_SUCCESS]) - def on_hci_le_remove_device_from_white_list_command(self, command): + def on_hci_le_remove_device_from_filter_accept_list_command(self, _command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.17 LE Remove Device From White List Command + See Bluetooth spec Vol 2, Part E - 7.8.17 LE Remove Device From Filter Accept + List Command ''' return bytes([HCI_SUCCESS]) @@ -761,33 +1064,41 @@ class Controller: ''' # First, say that the command is pending - self.send_hci_packet(HCI_Command_Status_Event( - status = HCI_COMMAND_STATUS_PENDING, - num_hci_command_packets = 1, - command_opcode = command.op_code - )) + self.send_hci_packet( + HCI_Command_Status_Event( + status=HCI_COMMAND_STATUS_PENDING, + num_hci_command_packets=1, + command_opcode=command.op_code, + ) + ) # Then send the remote features - self.send_hci_packet(HCI_LE_Read_Remote_Features_Complete_Event( - status = HCI_SUCCESS, - connection_handle = 0, - le_features = bytes.fromhex('dd40000000000000') - )) + self.send_hci_packet( + HCI_LE_Read_Remote_Features_Complete_Event( + status=HCI_SUCCESS, + connection_handle=0, + le_features=bytes.fromhex('dd40000000000000'), + ) + ) - def on_hci_le_rand_command(self, command): + def on_hci_le_rand_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.8.23 LE Rand Command ''' return bytes([HCI_SUCCESS]) + struct.pack('Q', random.randint(0, 1 << 64)) - def on_hci_le_start_encryption_command(self, command): + def on_hci_le_enable_encryption_command(self, command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.24 LE Start Encryption Command + See Bluetooth spec Vol 2, Part E - 7.8.24 LE Enable Encryption Command ''' # Check the parameters - if not (connection := self.find_central_connection_by_handle(command.connection_handle)): - logger.warn('connection not found') + if not ( + connection := self.find_central_connection_by_handle( + command.connection_handle + ) + ): + logger.warning('connection not found') return bytes([HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR]) # Notify that the connection is now encrypted @@ -796,57 +1107,68 @@ class Controller: connection.peer_address, command.random_number, command.encrypted_diversifier, - command.long_term_key + command.long_term_key, + ) + + self.send_hci_packet( + HCI_Command_Status_Event( + status=HCI_COMMAND_STATUS_PENDING, + num_hci_command_packets=1, + command_opcode=command.op_code, + ) ) - self.send_hci_packet(HCI_Command_Status_Event( - status = HCI_COMMAND_STATUS_PENDING, - num_hci_command_packets = 1, - command_opcode = command.op_code - )) + return None - def on_hci_le_read_supported_states_command(self, command): + def on_hci_le_read_supported_states_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.8.27 LE Read Supported States Command ''' return bytes([HCI_SUCCESS]) + self.le_states - def on_hci_le_read_suggested_default_data_length_command(self, command): + def on_hci_le_read_suggested_default_data_length_command(self, _command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.34 LE Read Suggested Default Data Length Command + See Bluetooth spec Vol 2, Part E - 7.8.34 LE Read Suggested Default Data Length + Command ''' - return struct.pack('<BHH', - HCI_SUCCESS, - self.suggested_max_tx_octets, - self.suggested_max_tx_time) + return struct.pack( + '<BHH', + HCI_SUCCESS, + self.suggested_max_tx_octets, + self.suggested_max_tx_time, + ) def on_hci_le_write_suggested_default_data_length_command(self, command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.35 LE Write Suggested Default Data Length Command + See Bluetooth spec Vol 2, Part E - 7.8.35 LE Write Suggested Default Data Length + Command ''' - self.suggested_max_tx_octets, self.suggested_max_tx_time = struct.unpack('<HH', command.parameters[:4]) + self.suggested_max_tx_octets, self.suggested_max_tx_time = struct.unpack( + '<HH', command.parameters[:4] + ) return bytes([HCI_SUCCESS]) - def on_hci_le_read_local_p_256_public_key_command(self, command): + def on_hci_le_read_local_p_256_public_key_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.8.36 LE Read P-256 Public Key Command ''' # TODO create key and send HCI_LE_Read_Local_P-256_Public_Key_Complete event return bytes([HCI_SUCCESS]) - def on_hci_le_add_device_to_resolving_list_command(self, command): + def on_hci_le_add_device_to_resolving_list_command(self, _command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.38 LE Add Device To Resolving List Command + See Bluetooth spec Vol 2, Part E - 7.8.38 LE Add Device To Resolving List + Command ''' return bytes([HCI_SUCCESS]) - def on_hci_le_clear_resolving_list_command(self, command): + def on_hci_le_clear_resolving_list_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.8.40 LE Clear Resolving List Command ''' return bytes([HCI_SUCCESS]) - def on_hci_le_read_resolving_list_size_command(self, command): + def on_hci_le_read_resolving_list_size_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.8.41 LE Read Resolving List Size Command ''' @@ -854,12 +1176,13 @@ class Controller: def on_hci_le_set_address_resolution_enable_command(self, command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.44 LE Set Address Resolution Enable Command + See Bluetooth spec Vol 2, Part E - 7.8.44 LE Set Address Resolution Enable + Command ''' ret = HCI_SUCCESS - if command.address_resolution == 1: + if command.address_resolution_enable == 1: self.le_address_resolution = True - elif command.address_resolution == 0: + elif command.address_resolution_enable == 0: self.le_address_resolution = False else: ret = HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR @@ -867,21 +1190,36 @@ class Controller: def on_hci_le_set_resolvable_private_address_timeout_command(self, command): ''' - See Bluetooth spec Vol 2, Part E - 7.8.45 LE Set Resolvable Private Address Timeout Command + See Bluetooth spec Vol 2, Part E - 7.8.45 LE Set Resolvable Private Address + Timeout Command ''' self.le_rpa_timeout = command.rpa_timeout return bytes([HCI_SUCCESS]) - def on_hci_le_read_maximum_data_length_command(self, command): + def on_hci_le_read_maximum_data_length_command(self, _command): ''' See Bluetooth spec Vol 2, Part E - 7.8.46 LE Read Maximum Data Length Command ''' - return struct.pack('<BHHHH', - HCI_SUCCESS, - self.supported_max_tx_octets, - self.supported_max_tx_time, - self.supported_max_rx_octets, - self.supported_max_rx_time) + return struct.pack( + '<BHHHH', + HCI_SUCCESS, + self.supported_max_tx_octets, + self.supported_max_tx_time, + self.supported_max_rx_octets, + self.supported_max_rx_time, + ) + + def on_hci_le_read_phy_command(self, command): + ''' + See Bluetooth spec Vol 2, Part E - 7.8.47 LE Read PHY Command + ''' + return struct.pack( + '<BHBB', + HCI_SUCCESS, + command.connection_handle, + HCI_LE_1M_PHY, + HCI_LE_1M_PHY, + ) def on_hci_le_set_default_phy_command(self, command): ''' @@ -889,7 +1227,13 @@ class Controller: ''' self.default_phy = { 'all_phys': command.all_phys, - 'tx_phys': command.tx_phys, - 'rx_phys': command.rx_phys + 'tx_phys': command.tx_phys, + 'rx_phys': command.rx_phys, } return bytes([HCI_SUCCESS]) + + def on_hci_le_read_transmit_power_command(self, _command): + ''' + See Bluetooth spec Vol 2, Part E - 7.8.74 LE Read Transmit Power Command + ''' + return struct.pack('<BBB', HCI_SUCCESS, 0, 0) diff --git a/bumble/core.py b/bumble/core.py index 746a601..1cc10ec 100644 --- a/bumble/core.py +++ b/bumble/core.py @@ -15,7 +15,9 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import struct +from typing import List, Optional, Tuple, Union, cast from .company_ids import COMPANY_IDENTIFIERS @@ -23,6 +25,8 @@ from .company_ids import COMPANY_IDENTIFIERS # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off + BT_CENTRAL_ROLE = 0 BT_PERIPHERAL_ROLE = 1 @@ -30,6 +34,9 @@ BT_BR_EDR_TRANSPORT = 0 BT_LE_TRANSPORT = 1 +# fmt: on + + # ----------------------------------------------------------------------------- # Utils # ----------------------------------------------------------------------------- @@ -58,17 +65,25 @@ def padded_bytes(buffer, size): return buffer + bytes(padding_size) +def get_dict_key_by_value(dictionary, value): + for key, val in dictionary.items(): + if val == value: + return key + return None + + # ----------------------------------------------------------------------------- # Exceptions # ----------------------------------------------------------------------------- class BaseError(Exception): - """ Base class for errors with an error code, error name and namespace""" + """Base class for errors with an error code, error name and namespace""" + def __init__(self, error_code, error_namespace='', error_name='', details=''): super().__init__() - self.error_code = error_code + self.error_code = error_code self.error_namespace = error_namespace - self.error_name = error_name - self.details = details + self.error_name = error_name + self.details = details def __str__(self): if self.error_namespace: @@ -84,22 +99,40 @@ class BaseError(Exception): class ProtocolError(BaseError): - """ Protocol Error """ + """Protocol Error""" + +class TimeoutError(Exception): # pylint: disable=redefined-builtin + """Timeout Error""" -class TimeoutError(Exception): - """ Timeout Error """ + +class CommandTimeoutError(Exception): + """Command Timeout Error""" class InvalidStateError(Exception): - """ Invalid State Error """ + """Invalid State Error""" + +class ConnectionError(BaseError): # pylint: disable=redefined-builtin + """Connection Error""" -class ConnectionError(BaseError): - """ Connection Error """ - FAILURE = 0x01 + FAILURE = 0x01 CONNECTION_REFUSED = 0x02 + def __init__( + self, + error_code, + transport, + peer_address, + error_namespace='', + error_name='', + details='', + ): + super().__init__(error_code, error_namespace, error_name, details) + self.transport = transport + self.peer_address = peer_address + # ----------------------------------------------------------------------------- # UUID @@ -111,27 +144,42 @@ class ConnectionError(BaseError): class UUID: ''' See Bluetooth spec Vol 3, Part B - 2.5.1 UUID + + Note that this class expects and works in little-endian byte-order throughout. + The exception is when interacting with strings, which are in big-endian byte-order. ''' - BASE_UUID = bytes.fromhex('00001000800000805F9B34FB') - UUIDS = [] # Registry of all instances created - def __init__(self, uuid_str_or_int, name = None): - if type(uuid_str_or_int) is int: + BASE_UUID = bytes.fromhex('00001000800000805F9B34FB')[::-1] # little-endian + UUIDS: List[UUID] = [] # Registry of all instances created + + uuid_bytes: bytes + name: Optional[str] + + def __init__( + self, uuid_str_or_int: Union[str, int], name: Optional[str] = None + ) -> None: + if isinstance(uuid_str_or_int, int): self.uuid_bytes = struct.pack('<H', uuid_str_or_int) else: if len(uuid_str_or_int) == 36: - if uuid_str_or_int[8] != '-' or uuid_str_or_int[13] != '-' or uuid_str_or_int[18] != '-' or uuid_str_or_int[23] != '-': + if ( + uuid_str_or_int[8] != '-' + or uuid_str_or_int[13] != '-' + or uuid_str_or_int[18] != '-' + or uuid_str_or_int[23] != '-' + ): raise ValueError('invalid UUID format') uuid_str = uuid_str_or_int.replace('-', '') else: uuid_str = uuid_str_or_int if len(uuid_str) != 32 and len(uuid_str) != 8 and len(uuid_str) != 4: - raise ValueError('invalid UUID format') + raise ValueError(f"invalid UUID format: {uuid_str}") self.uuid_bytes = bytes(reversed(bytes.fromhex(uuid_str))) self.name = name - def register(self): - # Register this object in the class registry, and update the entry's name if it wasn't set already + def register(self) -> UUID: + # Register this object in the class registry, and update the entry's name if + # it wasn't set already for uuid in self.UUIDS: if self == uuid: if uuid.name is None: @@ -142,102 +190,102 @@ class UUID: return self @classmethod - def from_bytes(cls, uuid_bytes, name = None): - if len(uuid_bytes) in {2, 4, 16}: + def from_bytes(cls, uuid_bytes: bytes, name: Optional[str] = None) -> UUID: + if len(uuid_bytes) in (2, 4, 16): self = cls.__new__(cls) self.uuid_bytes = uuid_bytes self.name = name return self.register() - else: - raise ValueError('only 2, 4 and 16 bytes are allowed') + + raise ValueError('only 2, 4 and 16 bytes are allowed') @classmethod - def from_16_bits(cls, uuid_16, name = None): + def from_16_bits(cls, uuid_16: int, name: Optional[str] = None) -> UUID: return cls.from_bytes(struct.pack('<H', uuid_16), name) @classmethod - def from_32_bits(cls, uuid_32, name = None): + def from_32_bits(cls, uuid_32: int, name: Optional[str] = None) -> UUID: return cls.from_bytes(struct.pack('<I', uuid_32), name) @classmethod - def parse_uuid(cls, bytes, offset): - return len(bytes), cls.from_bytes(bytes[offset:]) + def parse_uuid(cls, uuid_as_bytes: bytes, offset: int) -> Tuple[int, UUID]: + return len(uuid_as_bytes), cls.from_bytes(uuid_as_bytes[offset:]) @classmethod - def parse_uuid_2(cls, bytes, offset): - return offset + 2, cls.from_bytes(bytes[offset:offset + 2]) + def parse_uuid_2(cls, uuid_as_bytes: bytes, offset: int) -> Tuple[int, UUID]: + return offset + 2, cls.from_bytes(uuid_as_bytes[offset : offset + 2]) - def to_bytes(self, force_128 = False): - if len(self.uuid_bytes) == 16 or not force_128: + def to_bytes(self, force_128: bool = False) -> bytes: + ''' + Serialize UUID in little-endian byte-order + ''' + if not force_128: return self.uuid_bytes + + if len(self.uuid_bytes) == 2: + return self.BASE_UUID + self.uuid_bytes + bytes([0, 0]) elif len(self.uuid_bytes) == 4: - return self.uuid_bytes + UUID.BASE_UUID + return self.BASE_UUID + self.uuid_bytes + elif len(self.uuid_bytes) == 16: + return self.uuid_bytes else: - return self.uuid_bytes + bytes([0, 0]) + UUID.BASE_UUID + assert False, "unreachable" - def to_pdu_bytes(self): + def to_pdu_bytes(self) -> bytes: ''' Convert to bytes for use in an ATT PDU. According to Vol 3, Part F - 3.2.1 Attribute Type: "All 32-bit Attribute UUIDs shall be converted to 128-bit UUIDs when the Attribute UUID is contained in an ATT PDU." ''' - return self.to_bytes(force_128 = (len(self.uuid_bytes) == 4)) + return self.to_bytes(force_128=(len(self.uuid_bytes) == 4)) - def to_hex_str(self): + def to_hex_str(self, separator: str = '') -> str: if len(self.uuid_bytes) == 2 or len(self.uuid_bytes) == 4: return bytes(reversed(self.uuid_bytes)).hex().upper() - else: - return ''.join([ + + return separator.join( + [ bytes(reversed(self.uuid_bytes[12:16])).hex(), bytes(reversed(self.uuid_bytes[10:12])).hex(), bytes(reversed(self.uuid_bytes[8:10])).hex(), bytes(reversed(self.uuid_bytes[6:8])).hex(), - bytes(reversed(self.uuid_bytes[0:6])).hex() - ]).upper() + bytes(reversed(self.uuid_bytes[0:6])).hex(), + ] + ).upper() - def __bytes__(self): + def __bytes__(self) -> bytes: return self.to_bytes() - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, UUID): - return self.to_bytes(force_128 = True) == other.to_bytes(force_128 = True) - elif type(other) is str: + return self.to_bytes(force_128=True) == other.to_bytes(force_128=True) + + if isinstance(other, str): return UUID(other) == self return False - def __hash__(self): + def __hash__(self) -> int: return hash(self.uuid_bytes) - def __str__(self): + def __str__(self) -> str: + result = self.to_hex_str(separator='-') if len(self.uuid_bytes) == 2: - v = struct.unpack('<H', self.uuid_bytes)[0] - result = f'UUID-16:{v:04X}' + result = 'UUID-16:' + result elif len(self.uuid_bytes) == 4: - v = struct.unpack('<I', self.uuid_bytes)[0] - result = f'UUID-32:{v:08X}' - else: - result = '-'.join([ - bytes(reversed(self.uuid_bytes[12:16])).hex(), - bytes(reversed(self.uuid_bytes[10:12])).hex(), - bytes(reversed(self.uuid_bytes[8:10])).hex(), - bytes(reversed(self.uuid_bytes[6:8])).hex(), - bytes(reversed(self.uuid_bytes[0:6])).hex() - ]).upper() + result = 'UUID-32:' + result if self.name is not None: - return result + f' ({self.name})' - else: - return result - - def __repr__(self): - return str(self) + result += f' ({self.name})' + return result # ----------------------------------------------------------------------------- # Common UUID constants # ----------------------------------------------------------------------------- +# fmt: off +# pylint: disable=line-too-long # Protocol Identifiers BT_SDP_PROTOCOL_ID = UUID.from_16_bits(0x0001, 'SDP') @@ -343,11 +391,17 @@ BT_HDP_SERVICE = UUID.from_16_bits(0x1400, BT_HDP_SOURCE_SERVICE = UUID.from_16_bits(0x1401, 'HDP Source') BT_HDP_SINK_SERVICE = UUID.from_16_bits(0x1402, 'HDP Sink') +# fmt: on +# pylint: enable=line-too-long + # ----------------------------------------------------------------------------- # DeviceClass # ----------------------------------------------------------------------------- class DeviceClass: + # fmt: off + # pylint: disable=line-too-long + # Major Service Classes (flags combined with OR) LIMITED_DISCOVERABLE_MODE_SERVICE_CLASS = (1 << 0) LE_AUDIO_SERVICE_CLASS = (1 << 1) @@ -515,11 +569,18 @@ class DeviceClass: PERIPHERAL_MAJOR_DEVICE_CLASS: PERIPHERAL_MINOR_DEVICE_CLASS_NAMES } + # fmt: on + # pylint: enable=line-too-long + @staticmethod def split_class_of_device(class_of_device): # Split the bit fields of the composite class of device value into: # (service_classes, major_device_class, minor_device_class) - return ((class_of_device >> 13 & 0x7FF), (class_of_device >> 8 & 0x1F), (class_of_device >> 2 & 0x3F)) + return ( + (class_of_device >> 13 & 0x7FF), + (class_of_device >> 8 & 0x1F), + (class_of_device >> 2 & 0x3F), + ) @staticmethod def pack_class_of_device(service_classes, major_device_class, minor_device_class): @@ -527,7 +588,9 @@ class DeviceClass: @staticmethod def service_class_labels(service_class_flags): - return bit_flags_to_strings(service_class_flags, DeviceClass.SERVICE_CLASS_LABELS) + return bit_flags_to_strings( + service_class_flags, DeviceClass.SERVICE_CLASS_LABELS + ) @staticmethod def major_device_class_name(device_class): @@ -544,7 +607,15 @@ class DeviceClass: # ----------------------------------------------------------------------------- # Advertising Data # ----------------------------------------------------------------------------- +AdvertisingObject = Union[ + List[UUID], Tuple[UUID, bytes], bytes, str, int, Tuple[int, int], Tuple[int, bytes] +] + + class AdvertisingData: + # fmt: off + # pylint: disable=line-too-long + # This list is only partial, it still needs to be filled in from the spec FLAGS = 0x01 INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS = 0x02 @@ -656,7 +727,14 @@ class AdvertisingData: BR_EDR_CONTROLLER_FLAG = 0x08 BR_EDR_HOST_FLAG = 0x10 - def __init__(self, ad_structures = []): + ad_structures: List[Tuple[int, bytes]] + + # fmt: on + # pylint: enable=line-too-long + + def __init__(self, ad_structures: Optional[List[Tuple[int, bytes]]] = None) -> None: + if ad_structures is None: + ad_structures = [] self.ad_structures = ad_structures[:] @staticmethod @@ -667,36 +745,36 @@ class AdvertisingData: @staticmethod def flags_to_string(flags, short=False): - flag_names = [ - 'LE Limited', - 'LE General', - 'No BR/EDR', - 'BR/EDR C', - 'BR/EDR H' - ] if short else [ - 'LE Limited Discoverable Mode', - 'LE General Discoverable Mode', - 'BR/EDR Not Supported', - 'Simultaneous LE and BR/EDR (Controller)', - 'Simultaneous LE and BR/EDR (Host)' - ] + flag_names = ( + ['LE Limited', 'LE General', 'No BR/EDR', 'BR/EDR C', 'BR/EDR H'] + if short + else [ + 'LE Limited Discoverable Mode', + 'LE General Discoverable Mode', + 'BR/EDR Not Supported', + 'Simultaneous LE and BR/EDR (Controller)', + 'Simultaneous LE and BR/EDR (Host)', + ] + ) return ','.join(bit_flags_to_strings(flags, flag_names)) @staticmethod - def uuid_list_to_objects(ad_data, uuid_size): + def uuid_list_to_objects(ad_data: bytes, uuid_size: int) -> List[UUID]: uuids = [] offset = 0 - while (uuid_size * (offset + 1)) <= len(ad_data): - uuids.append(UUID.from_bytes(ad_data[offset:offset + uuid_size])) + while (offset + uuid_size) <= len(ad_data): + uuids.append(UUID.from_bytes(ad_data[offset : offset + uuid_size])) offset += uuid_size return uuids @staticmethod def uuid_list_to_string(ad_data, uuid_size): - return ', '.join([ - str(uuid) - for uuid in AdvertisingData.uuid_list_to_objects(ad_data, uuid_size) - ]) + return ', '.join( + [ + str(uuid) + for uuid in AdvertisingData.uuid_list_to_objects(ad_data, uuid_size) + ] + ) @staticmethod def ad_data_to_string(ad_type, ad_data): @@ -756,40 +834,65 @@ class AdvertisingData: return f'[{ad_type_str}]: {ad_data_str}' + # pylint: disable=too-many-return-statements @staticmethod - def ad_data_to_object(ad_type, ad_data): - if ad_type in { + def ad_data_to_object(ad_type: int, ad_data: bytes) -> AdvertisingObject: + if ad_type in ( AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, - AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS - }: + AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, + AdvertisingData.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS, + ): return AdvertisingData.uuid_list_to_objects(ad_data, 2) - elif ad_type in { + + if ad_type in ( AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS, - AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS - }: + AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS, + AdvertisingData.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS, + ): return AdvertisingData.uuid_list_to_objects(ad_data, 4) - elif ad_type in { + + if ad_type in ( AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, - AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS - }: + AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, + AdvertisingData.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS, + ): return AdvertisingData.uuid_list_to_objects(ad_data, 16) - elif ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID: + + if ad_type == AdvertisingData.SERVICE_DATA_16_BIT_UUID: return (UUID.from_bytes(ad_data[:2]), ad_data[2:]) - elif ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID: + + if ad_type == AdvertisingData.SERVICE_DATA_32_BIT_UUID: return (UUID.from_bytes(ad_data[:4]), ad_data[4:]) - elif ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID: + + if ad_type == AdvertisingData.SERVICE_DATA_128_BIT_UUID: return (UUID.from_bytes(ad_data[:16]), ad_data[16:]) - elif ad_type in { + + if ad_type in ( AdvertisingData.SHORTENED_LOCAL_NAME, - AdvertisingData.COMPLETE_LOCAL_NAME - }: + AdvertisingData.COMPLETE_LOCAL_NAME, + AdvertisingData.URI, + ): return ad_data.decode("utf-8") - elif ad_type == AdvertisingData.TX_POWER_LEVEL: - return ad_data[0] - elif ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA: - return (struct.unpack_from('<H', ad_data, 0)[0], ad_data[2:]) - else: - return ad_data + + if ad_type in (AdvertisingData.TX_POWER_LEVEL, AdvertisingData.FLAGS): + return cast(int, struct.unpack('B', ad_data)[0]) + + if ad_type in ( + AdvertisingData.APPEARANCE, + AdvertisingData.ADVERTISING_INTERVAL, + ): + return cast(int, struct.unpack('<H', ad_data)[0]) + + if ad_type == AdvertisingData.CLASS_OF_DEVICE: + return cast(int, struct.unpack('<I', bytes([*ad_data, 0]))[0]) + + if ad_type == AdvertisingData.PERIPHERAL_CONNECTION_INTERVAL_RANGE: + return cast(Tuple[int, int], struct.unpack('<HH', ad_data)) + + if ad_type == AdvertisingData.MANUFACTURER_SPECIFIC_DATA: + return (cast(int, struct.unpack_from('<H', ad_data, 0)[0]), ad_data[2:]) + + return ad_data def append(self, data): offset = 0 @@ -798,30 +901,41 @@ class AdvertisingData: offset += 1 if length > 0: ad_type = data[offset] - ad_data = data[offset + 1:offset + length] + ad_data = data[offset + 1 : offset + length] self.ad_structures.append((ad_type, ad_data)) offset += length - def get(self, type_id, return_all=False, raw=True): + def get_all(self, type_id: int, raw: bool = False) -> List[AdvertisingObject]: ''' Get Advertising Data Structure(s) with a given type - If return_all is True, returns a (possibly empty) list of matches, - else returns the first entry, or None if no structure matches. + Returns a (possibly empty) list of matches. ''' - def process_ad_data(ad_data): + + def process_ad_data(ad_data: bytes) -> AdvertisingObject: return ad_data if raw else self.ad_data_to_object(type_id, ad_data) - if return_all: - return [process_ad_data(ad[1]) for ad in self.ad_structures if ad[0] == type_id] - else: - return next((process_ad_data(ad[1]) for ad in self.ad_structures if ad[0] == type_id), None) + return [process_ad_data(ad[1]) for ad in self.ad_structures if ad[0] == type_id] + + def get(self, type_id: int, raw: bool = False) -> Optional[AdvertisingObject]: + ''' + Get Advertising Data Structure(s) with a given type + + Returns the first entry, or None if no structure matches. + ''' + + all = self.get_all(type_id, raw=raw) + return all[0] if all else None def __bytes__(self): - return b''.join([bytes([len(x[1]) + 1, x[0]]) + x[1] for x in self.ad_structures]) + return b''.join( + [bytes([len(x[1]) + 1, x[0]]) + x[1] for x in self.ad_structures] + ) def to_string(self, separator=', '): - return separator.join([AdvertisingData.ad_data_to_string(x[0], x[1]) for x in self.ad_structures]) + return separator.join( + [AdvertisingData.ad_data_to_string(x[0], x[1]) for x in self.ad_structures] + ) def __str__(self): return self.to_string() @@ -831,13 +945,17 @@ class AdvertisingData: # Connection Parameters # ----------------------------------------------------------------------------- class ConnectionParameters: - def __init__(self, connection_interval, connection_latency, supervision_timeout): + def __init__(self, connection_interval, peripheral_latency, supervision_timeout): self.connection_interval = connection_interval - self.connection_latency = connection_latency + self.peripheral_latency = peripheral_latency self.supervision_timeout = supervision_timeout def __str__(self): - return f'ConnectionParameters(connection_interval={self.connection_interval}, connection_latency={self.connection_latency}, supervision_timeout={self.supervision_timeout}' + return ( + f'ConnectionParameters(connection_interval={self.connection_interval}, ' + f'peripheral_latency={self.peripheral_latency}, ' + f'supervision_timeout={self.supervision_timeout}' + ) # ----------------------------------------------------------------------------- diff --git a/bumble/crypto.py b/bumble/crypto.py index 4f13476..757594f 100644 --- a/bumble/crypto.py +++ b/bumble/crypto.py @@ -24,19 +24,16 @@ import logging import operator import platform + if platform.system() != 'Emscripten': import secrets - from cryptography.hazmat.primitives.ciphers import ( - Cipher, - algorithms, - modes - ) + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.asymmetric.ec import ( generate_private_key, ECDH, EllipticCurvePublicNumbers, EllipticCurvePrivateNumbers, - SECP256R1 + SECP256R1, ) from cryptography.hazmat.primitives import cmac else: @@ -66,16 +63,26 @@ class EccKey: d = int.from_bytes(d_bytes, byteorder='big', signed=False) x = int.from_bytes(x_bytes, byteorder='big', signed=False) y = int.from_bytes(y_bytes, byteorder='big', signed=False) - private_key = EllipticCurvePrivateNumbers(d, EllipticCurvePublicNumbers(x, y, SECP256R1())).private_key() + private_key = EllipticCurvePrivateNumbers( + d, EllipticCurvePublicNumbers(x, y, SECP256R1()) + ).private_key() return cls(private_key) @property def x(self): - return self.private_key.public_key().public_numbers().x.to_bytes(32, byteorder='big') + return ( + self.private_key.public_key() + .public_numbers() + .x.to_bytes(32, byteorder='big') + ) @property def y(self): - return self.private_key.public_key().public_numbers().y.to_bytes(32, byteorder='big') + return ( + self.private_key.public_key() + .public_numbers() + .y.to_bytes(32, byteorder='big') + ) def dh(self, public_key_x, public_key_y): x = int.from_bytes(public_key_x, byteorder='big', signed=False) @@ -92,7 +99,7 @@ class EccKey: # ----------------------------------------------------------------------------- def xor(x, y): - assert(len(x) == len(y)) + assert len(x) == len(y) return bytes(map(operator.xor, x, y)) @@ -118,7 +125,7 @@ def e(key, data): # ----------------------------------------------------------------------------- -def ah(k, r): +def ah(k, r): # pylint: disable=redefined-outer-name ''' See Bluetooth spec Vol 3, Part H - 2.2.2 Random Address Hash function ah ''' @@ -129,9 +136,10 @@ def ah(k, r): # ----------------------------------------------------------------------------- -def c1(k, r, preq, pres, iat, rat, ia, ra): +def c1(k, r, preq, pres, iat, rat, ia, ra): # pylint: disable=redefined-outer-name ''' - See Bluetooth spec, Vol 3, Part H - 2.2.3 Confirm value generation function c1 for LE Legacy Pairing + See Bluetooth spec, Vol 3, Part H - 2.2.3 Confirm value generation function c1 for + LE Legacy Pairing ''' p1 = bytes([iat, rat]) + preq + pres @@ -142,7 +150,8 @@ def c1(k, r, preq, pres, iat, rat, ia, ra): # ----------------------------------------------------------------------------- def s1(k, r1, r2): ''' - See Bluetooth spec, Vol 3, Part H - 2.2.4 Key generation function s1 for LE Legacy Pairing + See Bluetooth spec, Vol 3, Part H - 2.2.4 Key generation function s1 for LE Legacy + Pairing ''' return e(k, r2[0:8] + r1[0:8]) @@ -163,71 +172,95 @@ def aes_cmac(m, k): # ----------------------------------------------------------------------------- def f4(u, v, x, z): ''' - See Bluetooth spec, Vol 3, Part H - 2.2.6 LE Secure Connections Confirm Value Generation Function f4 + See Bluetooth spec, Vol 3, Part H - 2.2.6 LE Secure Connections Confirm Value + Generation Function f4 ''' - return bytes(reversed(aes_cmac(bytes(reversed(u)) + bytes(reversed(v)) + z, bytes(reversed(x))))) + return bytes( + reversed( + aes_cmac(bytes(reversed(u)) + bytes(reversed(v)) + z, bytes(reversed(x))) + ) + ) # ----------------------------------------------------------------------------- def f5(w, n1, n2, a1, a2): ''' - See Bluetooth spec, Vol 3, Part H - 2.2.7 LE Secure Connections Key Generation Function f5 + See Bluetooth spec, Vol 3, Part H - 2.2.7 LE Secure Connections Key Generation + Function f5 NOTE: this returns a tuple: (MacKey, LTK) in little-endian byte order ''' salt = bytes.fromhex('6C888391AAF5A53860370BDB5A6083BE') t = aes_cmac(bytes(reversed(w)), salt) - key_id = bytes([0x62, 0x74, 0x6c, 0x65]) + key_id = bytes([0x62, 0x74, 0x6C, 0x65]) return ( - bytes(reversed(aes_cmac( - bytes([0]) + - key_id + - bytes(reversed(n1)) + - bytes(reversed(n2)) + - bytes(reversed(a1)) + - bytes(reversed(a2)) + - bytes([1, 0]), - t - ))), - bytes(reversed(aes_cmac( - bytes([1]) + - key_id + - bytes(reversed(n1)) + - bytes(reversed(n2)) + - bytes(reversed(a1)) + - bytes(reversed(a2)) + - bytes([1, 0]), - t - ))) + bytes( + reversed( + aes_cmac( + bytes([0]) + + key_id + + bytes(reversed(n1)) + + bytes(reversed(n2)) + + bytes(reversed(a1)) + + bytes(reversed(a2)) + + bytes([1, 0]), + t, + ) + ) + ), + bytes( + reversed( + aes_cmac( + bytes([1]) + + key_id + + bytes(reversed(n1)) + + bytes(reversed(n2)) + + bytes(reversed(a1)) + + bytes(reversed(a2)) + + bytes([1, 0]), + t, + ) + ) + ), ) # ----------------------------------------------------------------------------- -def f6(w, n1, n2, r, io_cap, a1, a2): +def f6(w, n1, n2, r, io_cap, a1, a2): # pylint: disable=redefined-outer-name ''' - See Bluetooth spec, Vol 3, Part H - 2.2.8 LE Secure Connections Check Value Generation Function f6 + See Bluetooth spec, Vol 3, Part H - 2.2.8 LE Secure Connections Check Value + Generation Function f6 ''' - return bytes(reversed(aes_cmac( - bytes(reversed(n1)) + - bytes(reversed(n2)) + - bytes(reversed(r)) + - bytes(reversed(io_cap)) + - bytes(reversed(a1)) + - bytes(reversed(a2)), - bytes(reversed(w)) - ))) + return bytes( + reversed( + aes_cmac( + bytes(reversed(n1)) + + bytes(reversed(n2)) + + bytes(reversed(r)) + + bytes(reversed(io_cap)) + + bytes(reversed(a1)) + + bytes(reversed(a2)), + bytes(reversed(w)), + ) + ) + ) # ----------------------------------------------------------------------------- def g2(u, v, x, y): ''' - See Bluetooth spec, Vol 3, Part H - 2.2.9 LE Secure Connections Numeric Comparison Value Generation Function g2 + See Bluetooth spec, Vol 3, Part H - 2.2.9 LE Secure Connections Numeric Comparison + Value Generation Function g2 ''' return int.from_bytes( - aes_cmac(bytes(reversed(u)) + bytes(reversed(v)) + bytes(reversed(y)), bytes(reversed(x)))[-4:], - byteorder='big' + aes_cmac( + bytes(reversed(u)) + bytes(reversed(v)) + bytes(reversed(y)), + bytes(reversed(x)), + )[-4:], + byteorder='big', ) + # ----------------------------------------------------------------------------- def h6(w, key_id): ''' @@ -235,6 +268,7 @@ def h6(w, key_id): ''' return aes_cmac(key_id, w) + # ----------------------------------------------------------------------------- def h7(salt, w): ''' diff --git a/bumble/decoder.py b/bumble/decoder.py new file mode 100644 index 0000000..2eb70bc --- /dev/null +++ b/bumble/decoder.py @@ -0,0 +1,416 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Constants +# ----------------------------------------------------------------------------- +# fmt: off + +WL = [-60, -30, 58, 172, 334, 538, 1198, 3042] +RL42 = [0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0] +ILB = [ + 2048, + 2093, + 2139, + 2186, + 2233, + 2282, + 2332, + 2383, + 2435, + 2489, + 2543, + 2599, + 2656, + 2714, + 2774, + 2834, + 2896, + 2960, + 3025, + 3091, + 3158, + 3228, + 3298, + 3371, + 3444, + 3520, + 3597, + 3676, + 3756, + 3838, + 3922, + 4008, +] +WH = [0, -214, 798] +RH2 = [2, 1, 2, 1] +# Values in QM2/QM4/QM6 left shift three bits than original g722 specification. +QM2 = [-7408, -1616, 7408, 1616] +QM4 = [ + 0, + -20456, + -12896, + -8968, + -6288, + -4240, + -2584, + -1200, + 20456, + 12896, + 8968, + 6288, + 4240, + 2584, + 1200, + 0, +] +QM6 = [ + -136, + -136, + -136, + -136, + -24808, + -21904, + -19008, + -16704, + -14984, + -13512, + -12280, + -11192, + -10232, + -9360, + -8576, + -7856, + -7192, + -6576, + -6000, + -5456, + -4944, + -4464, + -4008, + -3576, + -3168, + -2776, + -2400, + -2032, + -1688, + -1360, + -1040, + -728, + 24808, + 21904, + 19008, + 16704, + 14984, + 13512, + 12280, + 11192, + 10232, + 9360, + 8576, + 7856, + 7192, + 6576, + 6000, + 5456, + 4944, + 4464, + 4008, + 3576, + 3168, + 2776, + 2400, + 2032, + 1688, + 1360, + 1040, + 728, + 432, + 136, + -432, + -136, +] +QMF_COEFFS = [3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11] + +# fmt: on + + +# ----------------------------------------------------------------------------- +# Classes +# ----------------------------------------------------------------------------- +class G722Decoder(object): + """G.722 decoder with bitrate 64kbit/s. + + For the Blocks in the sub-band decoders, please refer to the G.722 + specification for the required information. G722 specification: + https://www.itu.int/rec/T-REC-G.722-201209-I + """ + + def __init__(self): + self._x = [0] * 24 + self._band = [Band(), Band()] + # The initial value in BLOCK 3L + self._band[0].det = 32 + # The initial value in BLOCK 3H + self._band[1].det = 8 + + def decode_frame(self, encoded_data) -> bytearray: + result_array = bytearray(len(encoded_data) * 4) + self.g722_decode(result_array, encoded_data) + return result_array + + def g722_decode(self, result_array, encoded_data) -> int: + """Decode the data frame using g722 decoder.""" + result_length = 0 + + for code in encoded_data: + higher_bits = (code >> 6) & 0x03 + lower_bits = code & 0x3F + + rlow = self.lower_sub_band_decoder(lower_bits) + rhigh = self.higher_sub_band_decoder(higher_bits) + + # Apply the receive QMF + self._x[:22] = self._x[2:] + self._x[22] = rlow + rhigh + self._x[23] = rlow - rhigh + + xout2 = sum(self._x[2 * i] * QMF_COEFFS[i] for i in range(12)) + xout1 = sum(self._x[2 * i + 1] * QMF_COEFFS[11 - i] for i in range(12)) + + result_length = self.update_decoded_result( + xout1, result_length, result_array + ) + result_length = self.update_decoded_result( + xout2, result_length, result_array + ) + + return result_length + + def update_decoded_result(self, xout, byte_length, byte_array) -> int: + result = (int)(xout >> 11) + bytes_result = result.to_bytes(2, 'little', signed=True) + byte_array[byte_length] = bytes_result[0] + byte_array[byte_length + 1] = bytes_result[1] + return byte_length + 2 + + def lower_sub_band_decoder(self, lower_bits) -> int: + """Lower sub-band decoder for last six bits.""" + + # Block 5L + # INVQBL + wd1 = lower_bits + wd2 = QM6[wd1] + wd1 >>= 2 + wd2 = (self._band[0].det * wd2) >> 15 + # RECONS + rlow = self._band[0].s + wd2 + + # Block 6L + # LIMIT + if rlow > 16383: + rlow = 16383 + elif rlow < -16384: + rlow = -16384 + + # Block 2L + # INVQAL + wd2 = QM4[wd1] + dlowt = (self._band[0].det * wd2) >> 15 + + # Block 3L + # LOGSCL + wd2 = RL42[wd1] + wd1 = (self._band[0].nb * 127) >> 7 + wd1 += WL[wd2] + + if wd1 < 0: + wd1 = 0 + elif wd1 > 18432: + wd1 = 18432 + + self._band[0].nb = wd1 + + # SCALEL + wd1 = (self._band[0].nb >> 6) & 31 + wd2 = 8 - (self._band[0].nb >> 11) + + if wd2 < 0: + wd3 = ILB[wd1] << -wd2 + else: + wd3 = ILB[wd1] >> wd2 + + self._band[0].det = wd3 << 2 + + # Block 4L + self._band[0].block4(dlowt) + + return rlow + + def higher_sub_band_decoder(self, higher_bits) -> int: + """Higher sub-band decoder for first two bits.""" + + # Block 2H + # INVQAH + wd2 = QM2[higher_bits] + dhigh = (self._band[1].det * wd2) >> 15 + + # Block 5H + # RECONS + rhigh = dhigh + self._band[1].s + + # Block 6H + # LIMIT + if rhigh > 16383: + rhigh = 16383 + elif rhigh < -16384: + rhigh = -16384 + + # Block 3H + # LOGSCH + wd2 = RH2[higher_bits] + wd1 = (self._band[1].nb * 127) >> 7 + wd1 += WH[wd2] + + if wd1 < 0: + wd1 = 0 + elif wd1 > 22528: + wd1 = 22528 + self._band[1].nb = wd1 + + # SCALEH + wd1 = (self._band[1].nb >> 6) & 31 + wd2 = 10 - (self._band[1].nb >> 11) + + if wd2 < 0: + wd3 = ILB[wd1] << -wd2 + else: + wd3 = ILB[wd1] >> wd2 + self._band[1].det = wd3 << 2 + + # Block 4H + self._band[1].block4(dhigh) + + return rhigh + + +# ----------------------------------------------------------------------------- +class Band(object): + """Structure for G722 decode proccessing.""" + + s: int = 0 + nb: int = 0 + det: int = 0 + + def __init__(self): + self._sp = 0 + self._sz = 0 + self._r = [0] * 3 + self._a = [0] * 3 + self._ap = [0] * 3 + self._p = [0] * 3 + self._d = [0] * 7 + self._b = [0] * 7 + self._bp = [0] * 7 + self._sg = [0] * 7 + + def saturate(self, amp: int) -> int: + if amp > 32767: + return 32767 + elif amp < -32768: + return -32768 + else: + return amp + + def block4(self, d: int) -> None: + """Block4 for both lower and higher sub-band decoder.""" + wd1 = 0 + wd2 = 0 + wd3 = 0 + + # RECONS + self._d[0] = d + self._r[0] = self.saturate(self.s + d) + + # PARREC + self._p[0] = self.saturate(self._sz + d) + + # UPPOL2 + for i in range(3): + self._sg[i] = (self._p[i]) >> 15 + wd1 = self.saturate((self._a[1]) << 2) + wd2 = -wd1 if self._sg[0] == self._sg[1] else wd1 + + if wd2 > 32767: + wd2 = 32767 + + wd3 = 128 if self._sg[0] == self._sg[2] else -128 + wd3 += wd2 >> 7 + wd3 += (self._a[2] * 32512) >> 15 + + if wd3 > 12288: + wd3 = 12288 + elif wd3 < -12288: + wd3 = -12288 + self._ap[2] = wd3 + + # UPPOL1 + self._sg[0] = (self._p[0]) >> 15 + self._sg[1] = (self._p[1]) >> 15 + wd1 = 192 if self._sg[0] == self._sg[1] else -192 + wd2 = (self._a[1] * 32640) >> 15 + + self._ap[1] = self.saturate(wd1 + wd2) + wd3 = self.saturate(15360 - self._ap[2]) + + if self._ap[1] > wd3: + self._ap[1] = wd3 + elif self._ap[1] < -wd3: + self._ap[1] = -wd3 + + # UPZERO + wd1 = 0 if d == 0 else 128 + self._sg[0] = d >> 15 + for i in range(1, 7): + self._sg[i] = (self._d[i]) >> 15 + wd2 = wd1 if self._sg[i] == self._sg[0] else -wd1 + wd3 = (self._b[i] * 32640) >> 15 + self._bp[i] = self.saturate(wd2 + wd3) + + # DELAYA + for i in range(6, 0, -1): + self._d[i] = self._d[i - 1] + self._b[i] = self._bp[i] + + for i in range(2, 0, -1): + self._r[i] = self._r[i - 1] + self._p[i] = self._p[i - 1] + self._a[i] = self._ap[i] + + # FILTEP + self._sp = 0 + for i in range(1, 3): + wd1 = self.saturate(self._r[i] + self._r[i]) + self._sp += (self._a[i] * wd1) >> 15 + self._sp = self.saturate(self._sp) + + # FILTEZ + self._sz = 0 + for i in range(6, 0, -1): + wd1 = self.saturate(self._d[i] + self._d[i]) + self._sz += (self._b[i] * wd1) >> 15 + self._sz = self.saturate(self._sz) + + # PREDIC + self.s = self.saturate(self._sp + self._sz) diff --git a/bumble/device.py b/bumble/device.py index 97ead5e..258a43d 100644 --- a/bumble/device.py +++ b/bumble/device.py @@ -15,22 +15,142 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations +from enum import IntEnum +import functools import json import asyncio import logging - -from .hci import * +from contextlib import asynccontextmanager, AsyncExitStack +from dataclasses import dataclass +from typing import Any, Callable, ClassVar, Dict, List, Optional, Tuple, Type, Union + +from .colors import color +from .att import ATT_CID, ATT_DEFAULT_MTU, ATT_PDU +from .gatt import Characteristic, Descriptor, Service +from .hci import ( + HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE, + HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE, + HCI_CENTRAL_ROLE, + HCI_COMMAND_STATUS_PENDING, + HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR, + HCI_DISPLAY_YES_NO_IO_CAPABILITY, + HCI_DISPLAY_ONLY_IO_CAPABILITY, + HCI_EXTENDED_INQUIRY_MODE, + HCI_GENERAL_INQUIRY_LAP, + HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR, + HCI_KEYBOARD_ONLY_IO_CAPABILITY, + HCI_LE_1M_PHY, + HCI_LE_1M_PHY_BIT, + HCI_LE_2M_PHY, + HCI_LE_2M_PHY_LE_SUPPORTED_FEATURE, + HCI_LE_CLEAR_RESOLVING_LIST_COMMAND, + HCI_LE_CODED_PHY, + HCI_LE_CODED_PHY_BIT, + HCI_LE_CODED_PHY_LE_SUPPORTED_FEATURE, + HCI_LE_EXTENDED_ADVERTISING_LE_SUPPORTED_FEATURE, + HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND, + HCI_LE_RAND_COMMAND, + HCI_LE_READ_PHY_COMMAND, + HCI_LE_SET_PHY_COMMAND, + HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS, + HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS, + HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS, + HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS, + HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY, + HCI_R2_PAGE_SCAN_REPETITION_MODE, + HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR, + HCI_SUCCESS, + HCI_WRITE_LE_HOST_SUPPORT_COMMAND, + Address, + HCI_Accept_Connection_Request_Command, + HCI_Authentication_Requested_Command, + HCI_Command_Status_Event, + HCI_Constant, + HCI_Create_Connection_Cancel_Command, + HCI_Create_Connection_Command, + HCI_Disconnect_Command, + HCI_Encryption_Change_Event, + HCI_Error, + HCI_IO_Capability_Request_Reply_Command, + HCI_Inquiry_Cancel_Command, + HCI_Inquiry_Command, + HCI_LE_Add_Device_To_Resolving_List_Command, + HCI_LE_Advertising_Report_Event, + HCI_LE_Clear_Resolving_List_Command, + HCI_LE_Connection_Update_Command, + HCI_LE_Create_Connection_Cancel_Command, + HCI_LE_Create_Connection_Command, + HCI_LE_Enable_Encryption_Command, + HCI_LE_Extended_Advertising_Report_Event, + HCI_LE_Extended_Create_Connection_Command, + HCI_LE_Rand_Command, + HCI_LE_Read_PHY_Command, + HCI_LE_Set_Advertising_Data_Command, + HCI_LE_Set_Advertising_Enable_Command, + HCI_LE_Set_Advertising_Parameters_Command, + HCI_LE_Set_Default_PHY_Command, + HCI_LE_Set_Extended_Scan_Enable_Command, + HCI_LE_Set_Extended_Scan_Parameters_Command, + HCI_LE_Set_PHY_Command, + HCI_LE_Set_Random_Address_Command, + HCI_LE_Set_Scan_Enable_Command, + HCI_LE_Set_Scan_Parameters_Command, + HCI_LE_Set_Scan_Response_Data_Command, + HCI_PIN_Code_Request_Reply_Command, + HCI_PIN_Code_Request_Negative_Reply_Command, + HCI_Read_BD_ADDR_Command, + HCI_Read_RSSI_Command, + HCI_Reject_Connection_Request_Command, + HCI_Remote_Name_Request_Command, + HCI_Switch_Role_Command, + HCI_Set_Connection_Encryption_Command, + HCI_StatusError, + HCI_User_Confirmation_Request_Negative_Reply_Command, + HCI_User_Confirmation_Request_Reply_Command, + HCI_User_Passkey_Request_Negative_Reply_Command, + HCI_User_Passkey_Request_Reply_Command, + HCI_Write_Class_Of_Device_Command, + HCI_Write_Extended_Inquiry_Response_Command, + HCI_Write_Inquiry_Mode_Command, + HCI_Write_LE_Host_Support_Command, + HCI_Write_Local_Name_Command, + HCI_Write_Scan_Enable_Command, + HCI_Write_Secure_Connections_Host_Support_Command, + HCI_Write_Simple_Pairing_Mode_Command, + OwnAddressType, + phy_list_to_bits, +) from .host import Host -from .gatt import * from .gap import GenericAccessService -from .core import AdvertisingData, BT_CENTRAL_ROLE, BT_PERIPHERAL_ROLE -from .utils import AsyncRunner, CompositeEventEmitter, setup_event_forwarding, composite_listener +from .core import ( + BT_BR_EDR_TRANSPORT, + BT_CENTRAL_ROLE, + BT_LE_TRANSPORT, + BT_PERIPHERAL_ROLE, + AdvertisingData, + CommandTimeoutError, + ConnectionPHY, + InvalidStateError, +) +from .utils import ( + AsyncRunner, + CompositeEventEmitter, + setup_event_forwarding, + composite_listener, +) +from .keys import ( + KeyStore, + PairingKeys, +) +from .pairing import PairingConfig from . import gatt_client from . import gatt_server from . import smp from . import sdp from . import l2cap -from . import keys +from . import core + # ----------------------------------------------------------------------------- # Logging @@ -40,20 +160,42 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- -DEVICE_DEFAULT_ADDRESS = '00:00:00:00:00:00' -DEVICE_DEFAULT_ADVERTISING_INTERVAL = 1000 # ms -DEVICE_DEFAULT_ADVERTISING_DATA = '' -DEVICE_DEFAULT_NAME = 'Bumble' -DEVICE_DEFAULT_INQUIRY_LENGTH = 8 # 10.24 seconds -DEVICE_DEFAULT_CLASS_OF_DEVICE = 0 -DEVICE_DEFAULT_SCAN_RESPONSE_DATA = b'' -DEVICE_DEFAULT_DATA_LENGTH = (27, 328, 27, 328) -DEVICE_DEFAULT_SCAN_INTERVAL = 60 # ms -DEVICE_DEFAULT_SCAN_WINDOW = 60 # ms -DEVICE_MIN_SCAN_INTERVAL = 25 -DEVICE_MAX_SCAN_INTERVAL = 10240 -DEVICE_MIN_SCAN_WINDOW = 25 -DEVICE_MAX_SCAN_WINDOW = 10240 +# fmt: off +# pylint: disable=line-too-long + +DEVICE_MIN_SCAN_INTERVAL = 25 +DEVICE_MAX_SCAN_INTERVAL = 10240 +DEVICE_MIN_SCAN_WINDOW = 25 +DEVICE_MAX_SCAN_WINDOW = 10240 +DEVICE_MIN_LE_RSSI = -127 +DEVICE_MAX_LE_RSSI = 20 + +DEVICE_DEFAULT_ADDRESS = '00:00:00:00:00:00' +DEVICE_DEFAULT_ADVERTISING_INTERVAL = 1000 # ms +DEVICE_DEFAULT_ADVERTISING_DATA = '' +DEVICE_DEFAULT_NAME = 'Bumble' +DEVICE_DEFAULT_INQUIRY_LENGTH = 8 # 10.24 seconds +DEVICE_DEFAULT_CLASS_OF_DEVICE = 0 +DEVICE_DEFAULT_SCAN_RESPONSE_DATA = b'' +DEVICE_DEFAULT_DATA_LENGTH = (27, 328, 27, 328) +DEVICE_DEFAULT_SCAN_INTERVAL = 60 # ms +DEVICE_DEFAULT_SCAN_WINDOW = 60 # ms +DEVICE_DEFAULT_CONNECT_TIMEOUT = None # No timeout +DEVICE_DEFAULT_CONNECT_SCAN_INTERVAL = 60 # ms +DEVICE_DEFAULT_CONNECT_SCAN_WINDOW = 60 # ms +DEVICE_DEFAULT_CONNECTION_INTERVAL_MIN = 15 # ms +DEVICE_DEFAULT_CONNECTION_INTERVAL_MAX = 30 # ms +DEVICE_DEFAULT_CONNECTION_MAX_LATENCY = 0 +DEVICE_DEFAULT_CONNECTION_SUPERVISION_TIMEOUT = 720 # ms +DEVICE_DEFAULT_CONNECTION_MIN_CE_LENGTH = 0 # ms +DEVICE_DEFAULT_CONNECTION_MAX_CE_LENGTH = 0 # ms +DEVICE_DEFAULT_L2CAP_COC_MTU = l2cap.L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU +DEVICE_DEFAULT_L2CAP_COC_MPS = l2cap.L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS +DEVICE_DEFAULT_L2CAP_COC_MAX_CREDITS = l2cap.L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS + +# fmt: on +# pylint: enable=line-too-long + # ----------------------------------------------------------------------------- # Classes @@ -61,28 +203,210 @@ DEVICE_MAX_SCAN_WINDOW = 10240 # ----------------------------------------------------------------------------- +class Advertisement: + address: Address + + TX_POWER_NOT_AVAILABLE = ( + HCI_LE_Extended_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE + ) + RSSI_NOT_AVAILABLE = HCI_LE_Extended_Advertising_Report_Event.RSSI_NOT_AVAILABLE + + @classmethod + def from_advertising_report(cls, report): + if isinstance(report, HCI_LE_Advertising_Report_Event.Report): + return LegacyAdvertisement.from_advertising_report(report) + + if isinstance(report, HCI_LE_Extended_Advertising_Report_Event.Report): + return ExtendedAdvertisement.from_advertising_report(report) + + return None + + # pylint: disable=line-too-long + def __init__( + self, + address, + rssi=HCI_LE_Extended_Advertising_Report_Event.RSSI_NOT_AVAILABLE, + is_legacy=False, + is_anonymous=False, + is_connectable=False, + is_directed=False, + is_scannable=False, + is_scan_response=False, + is_complete=True, + is_truncated=False, + primary_phy=0, + secondary_phy=0, + tx_power=HCI_LE_Extended_Advertising_Report_Event.TX_POWER_INFORMATION_NOT_AVAILABLE, + sid=0, + data=b'', + ): + self.address = address + self.rssi = rssi + self.is_legacy = is_legacy + self.is_anonymous = is_anonymous + self.is_connectable = is_connectable + self.is_directed = is_directed + self.is_scannable = is_scannable + self.is_scan_response = is_scan_response + self.is_complete = is_complete + self.is_truncated = is_truncated + self.primary_phy = primary_phy + self.secondary_phy = secondary_phy + self.tx_power = tx_power + self.sid = sid + self.data = AdvertisingData.from_bytes(data) + + +# ----------------------------------------------------------------------------- +class LegacyAdvertisement(Advertisement): + @classmethod + def from_advertising_report(cls, report): + return cls( + address=report.address, + rssi=report.rssi, + is_legacy=True, + is_connectable=report.event_type + in ( + HCI_LE_Advertising_Report_Event.ADV_IND, + HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND, + ), + is_directed=report.event_type + == HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND, + is_scannable=report.event_type + in ( + HCI_LE_Advertising_Report_Event.ADV_IND, + HCI_LE_Advertising_Report_Event.ADV_SCAN_IND, + ), + is_scan_response=report.event_type + == HCI_LE_Advertising_Report_Event.SCAN_RSP, + data=report.data, + ) + + +# ----------------------------------------------------------------------------- +class ExtendedAdvertisement(Advertisement): + @classmethod + def from_advertising_report(cls, report): + # fmt: off + # pylint: disable=line-too-long + return cls( + address = report.address, + rssi = report.rssi, + is_legacy = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.LEGACY_ADVERTISING_PDU_USED) != 0, + is_anonymous = report.address.address_type == HCI_LE_Extended_Advertising_Report_Event.ANONYMOUS_ADDRESS_TYPE, + is_connectable = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.CONNECTABLE_ADVERTISING) != 0, + is_directed = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.DIRECTED_ADVERTISING) != 0, + is_scannable = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.SCANNABLE_ADVERTISING) != 0, + is_scan_response = report.event_type & (1 << HCI_LE_Extended_Advertising_Report_Event.SCAN_RESPONSE) != 0, + is_complete = (report.event_type >> 5 & 3) == HCI_LE_Extended_Advertising_Report_Event.DATA_COMPLETE, + is_truncated = (report.event_type >> 5 & 3) == HCI_LE_Extended_Advertising_Report_Event.DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME, + primary_phy = report.primary_phy, + secondary_phy = report.secondary_phy, + tx_power = report.tx_power, + sid = report.advertising_sid, + data = report.data + ) + # fmt: on + + +# ----------------------------------------------------------------------------- class AdvertisementDataAccumulator: - def __init__(self): - self.advertising_data = AdvertisingData() - self.last_advertisement_type = None - self.connectable = False - self.flushable = False - - def update(self, data, advertisement_type): - if advertisement_type == HCI_LE_Advertising_Report_Event.SCAN_RSP: - if self.last_advertisement_type != HCI_LE_Advertising_Report_Event.SCAN_RSP: - self.advertising_data.append(data) - self.flushable = True + def __init__(self, passive=False): + self.passive = passive + self.last_advertisement = None + self.last_data = b'' + + def update(self, report): + advertisement = Advertisement.from_advertising_report(report) + if advertisement is None: + return None + + result = None + + if advertisement.is_scan_response: + if ( + self.last_advertisement is not None + and not self.last_advertisement.is_scan_response + ): + # This is the response to a scannable advertisement + result = Advertisement.from_advertising_report(report) + result.is_connectable = self.last_advertisement.is_connectable + result.is_scannable = True + result.data = AdvertisingData.from_bytes(self.last_data + report.data) + self.last_data = b'' else: - self.advertising_data = AdvertisingData.from_bytes(data) - self.flushable = self.last_advertisement_type != HCI_LE_Advertising_Report_Event.SCAN_RSP + if ( + self.passive + or (not advertisement.is_scannable) + or ( + self.last_advertisement is not None + and not self.last_advertisement.is_scan_response + ) + ): + # Don't wait for a scan response + result = Advertisement.from_advertising_report(report) + + self.last_data = report.data - if advertisement_type == HCI_LE_Advertising_Report_Event.ADV_IND or advertisement_type == HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND: - self.connectable = True - elif advertisement_type == HCI_LE_Advertising_Report_Event.ADV_SCAN_IND or advertisement_type == HCI_LE_Advertising_Report_Event.ADV_NONCONN_IND: - self.connectable = False + self.last_advertisement = advertisement - self.last_advertisement_type = advertisement_type + return result + + +# ----------------------------------------------------------------------------- +class AdvertisingType(IntEnum): + # fmt: off + # pylint: disable=line-too-long + UNDIRECTED_CONNECTABLE_SCANNABLE = 0x00 # Undirected, connectable, scannable + DIRECTED_CONNECTABLE_HIGH_DUTY = 0x01 # Directed, connectable, non-scannable + UNDIRECTED_SCANNABLE = 0x02 # Undirected, non-connectable, scannable + UNDIRECTED = 0x03 # Undirected, non-connectable, non-scannable + DIRECTED_CONNECTABLE_LOW_DUTY = 0x04 # Directed, connectable, non-scannable + # fmt: on + + @property + def has_data(self): + return self in ( + AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE, + AdvertisingType.UNDIRECTED_SCANNABLE, + AdvertisingType.UNDIRECTED, + ) + + @property + def is_connectable(self): + return self in ( + AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE, + AdvertisingType.DIRECTED_CONNECTABLE_HIGH_DUTY, + AdvertisingType.DIRECTED_CONNECTABLE_LOW_DUTY, + ) + + @property + def is_scannable(self): + return self in ( + AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE, + AdvertisingType.UNDIRECTED_SCANNABLE, + ) + + @property + def is_directed(self): + return self in ( + AdvertisingType.DIRECTED_CONNECTABLE_HIGH_DUTY, + AdvertisingType.DIRECTED_CONNECTABLE_LOW_DUTY, + ) + + +# ----------------------------------------------------------------------------- +class LePhyOptions: + # Coded PHY preference + ANY_CODED_PHY = 0 + PREFER_S_2_CODED_PHY = 1 + PREFER_S_8_CODED_PHY = 2 + + def __init__(self, coded_phy_preference=0): + self.coded_phy_preference = coded_phy_preference + + def __int__(self): + return self.coded_phy_preference & 3 # ----------------------------------------------------------------------------- @@ -99,28 +423,41 @@ class Peer: return self.gatt_client.services async def request_mtu(self, mtu): - return await self.gatt_client.request_mtu(mtu) + mtu = await self.gatt_client.request_mtu(mtu) + self.connection.emit('connection_att_mtu_update') + return mtu async def discover_service(self, uuid): return await self.gatt_client.discover_service(uuid) - async def discover_services(self, uuids = []): + async def discover_services(self, uuids=()): return await self.gatt_client.discover_services(uuids) async def discover_included_services(self, service): return await self.gatt_client.discover_included_services(service) - async def discover_characteristics(self, uuids = [], service = None): - return await self.gatt_client.discover_characteristics(uuids = uuids, service = service) + async def discover_characteristics(self, uuids=(), service=None): + return await self.gatt_client.discover_characteristics( + uuids=uuids, service=service + ) - async def discover_descriptors(self, characteristic = None, start_handle = None, end_handle = None): - return await self.gatt_client.discover_descriptors(characteristic, start_handle, end_handle) + async def discover_descriptors( + self, characteristic=None, start_handle=None, end_handle=None + ): + return await self.gatt_client.discover_descriptors( + characteristic, start_handle, end_handle + ) async def discover_attributes(self): return await self.gatt_client.discover_attributes() - async def subscribe(self, characteristic, subscriber=None): - return await self.gatt_client.subscribe(characteristic, subscriber) + async def subscribe(self, characteristic, subscriber=None, prefer_notify=True): + return await self.gatt_client.subscribe( + characteristic, subscriber, prefer_notify + ) + + async def unsubscribe(self, characteristic, subscriber=None): + return await self.gatt_client.unsubscribe(characteristic, subscriber) async def read_value(self, attribute): return await self.gatt_client.read_value(attribute) @@ -134,7 +471,7 @@ class Peer: def get_services_by_uuid(self, uuid): return self.gatt_client.get_services_by_uuid(uuid) - def get_characteristics_by_uuid(self, uuid, service = None): + def get_characteristics_by_uuid(self, uuid, service=None): return self.gatt_client.get_characteristics_by_uuid(uuid, service) def create_service_proxy(self, proxy_class): @@ -148,16 +485,59 @@ class Peer: await service.discover_characteristics() return self.create_service_proxy(proxy_class) + async def sustain(self, timeout=None): + await self.connection.sustain(timeout) + # [Classic only] async def request_name(self): return await self.connection.request_remote_name() + async def __aenter__(self): + await self.discover_services() + for service in self.services: + await service.discover_characteristics() + + return self + + async def __aexit__(self, exc_type, exc_value, traceback): + pass + def __str__(self): return f'{self.connection.peer_address} as {self.connection.role_name}' # ----------------------------------------------------------------------------- +@dataclass +class ConnectionParametersPreferences: + default: ClassVar[ConnectionParametersPreferences] + connection_interval_min: int = DEVICE_DEFAULT_CONNECTION_INTERVAL_MIN + connection_interval_max: int = DEVICE_DEFAULT_CONNECTION_INTERVAL_MAX + max_latency: int = DEVICE_DEFAULT_CONNECTION_MAX_LATENCY + supervision_timeout: int = DEVICE_DEFAULT_CONNECTION_SUPERVISION_TIMEOUT + min_ce_length: int = DEVICE_DEFAULT_CONNECTION_MIN_CE_LENGTH + max_ce_length: int = DEVICE_DEFAULT_CONNECTION_MAX_CE_LENGTH + + +ConnectionParametersPreferences.default = ConnectionParametersPreferences() + + +# ----------------------------------------------------------------------------- class Connection(CompositeEventEmitter): + device: Device + handle: int + transport: int + self_address: Address + peer_address: Address + peer_resolvable_address: Optional[Address] + role: int + encryption: int + authenticated: bool + sc: bool + link_key_type: int + gatt_client: gatt_client.Client + pairing_peer_io_capability: Optional[int] + pairing_peer_authentication_requirements: Optional[int] + @composite_listener class Listener: def on_disconnection(self, reason): @@ -169,6 +549,9 @@ class Connection(CompositeEventEmitter): def on_connection_parameters_update_failure(self, error): pass + def on_connection_data_length_change(self): + pass + def on_connection_phy_update(self): pass @@ -184,110 +567,249 @@ class Connection(CompositeEventEmitter): def on_connection_encryption_key_refresh(self): pass - def __init__(self, device, handle, transport, peer_address, peer_resolvable_address, role, parameters): + def __init__( + self, + device, + handle, + transport, + self_address, + peer_address, + peer_resolvable_address, + role, + parameters, + phy, + ): super().__init__() - self.device = device - self.handle = handle - self.transport = transport - self.peer_address = peer_address + self.device = device + self.handle = handle + self.transport = transport + self.self_address = self_address + self.peer_address = peer_address self.peer_resolvable_address = peer_resolvable_address - self.peer_name = None # Classic only - self.role = role - self.parameters = parameters - self.encryption = 0 - self.authenticated = False - self.phy = ConnectionPHY(HCI_LE_1M_PHY, HCI_LE_1M_PHY) - self.att_mtu = ATT_DEFAULT_MTU - self.data_length = DEVICE_DEFAULT_DATA_LENGTH - self.gatt_client = None # Per-connection client - self.gatt_server = device.gatt_server # By default, use the device's shared server + self.peer_name = None # Classic only + self.role = role + self.parameters = parameters + self.encryption = 0 + self.authenticated = False + self.sc = False + self.link_key_type = None + self.phy = phy + self.att_mtu = ATT_DEFAULT_MTU + self.data_length = DEVICE_DEFAULT_DATA_LENGTH + self.gatt_client = None # Per-connection client + self.gatt_server = ( + device.gatt_server + ) # By default, use the device's shared server + self.pairing_peer_io_capability = None + self.pairing_peer_authentication_requirements = None + + # [Classic only] + @classmethod + def incomplete(cls, device, peer_address, role): + """ + Instantiate an incomplete connection (ie. one waiting for a HCI Connection + Complete event). + Once received it shall be completed using the `.complete` method. + """ + return cls( + device, + None, + BT_BR_EDR_TRANSPORT, + device.public_address, + peer_address, + None, + role, + None, + None, + ) + + # [Classic only] + def complete(self, handle, parameters): + """ + Finish an incomplete connection upon completion. + """ + assert self.handle is None + assert self.transport == BT_BR_EDR_TRANSPORT + self.handle = handle + self.parameters = parameters @property def role_name(self): - return 'CENTRAL' if self.role == BT_CENTRAL_ROLE else 'PERIPHERAL' + if self.role is None: + return 'NOT-SET' + if self.role == BT_CENTRAL_ROLE: + return 'CENTRAL' + if self.role == BT_PERIPHERAL_ROLE: + return 'PERIPHERAL' + return f'UNKNOWN[{self.role}]' @property def is_encrypted(self): return self.encryption != 0 + @property + def is_incomplete(self) -> bool: + return self.handle is None + def send_l2cap_pdu(self, cid, pdu): self.device.send_l2cap_pdu(self.handle, cid, pdu) def create_l2cap_connector(self, psm): return self.device.create_l2cap_connector(self, psm) - async def disconnect(self, reason = HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR): - return await self.device.disconnect(self, reason) + async def open_l2cap_channel( + self, + psm, + max_credits=DEVICE_DEFAULT_L2CAP_COC_MAX_CREDITS, + mtu=DEVICE_DEFAULT_L2CAP_COC_MTU, + mps=DEVICE_DEFAULT_L2CAP_COC_MPS, + ): + return await self.device.open_l2cap_channel(self, psm, max_credits, mtu, mps) + + async def disconnect( + self, reason: int = HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR + ) -> None: + await self.device.disconnect(self, reason) - async def pair(self): + async def pair(self) -> None: return await self.device.pair(self) - def request_pairing(self): + def request_pairing(self) -> None: return self.device.request_pairing(self) # [Classic only] - async def authenticate(self): + async def authenticate(self) -> None: return await self.device.authenticate(self) - async def encrypt(self): - return await self.device.encrypt(self) + async def encrypt(self, enable: bool = True) -> None: + return await self.device.encrypt(self, enable) + + async def switch_role(self, role: int) -> None: + return await self.device.switch_role(self, role) + + async def sustain(self, timeout=None): + """Idles the current task waiting for a disconnect or timeout""" + + abort = asyncio.get_running_loop().create_future() + self.on('disconnection', abort.set_result) + self.on('disconnection_failure', abort.set_exception) + + try: + await asyncio.wait_for(self.device.abort_on('flush', abort), timeout) + except asyncio.TimeoutError: + pass + + self.remove_listener('disconnection', abort.set_result) + self.remove_listener('disconnection_failure', abort.set_exception) async def update_parameters( self, - conn_interval_min, - conn_interval_max, - conn_latency, - supervision_timeout + connection_interval_min, + connection_interval_max, + max_latency, + supervision_timeout, ): return await self.device.update_connection_parameters( self, - conn_interval_min, - conn_interval_max, - conn_latency, - supervision_timeout + connection_interval_min, + connection_interval_max, + max_latency, + supervision_timeout, ) + async def set_phy(self, tx_phys=None, rx_phys=None, phy_options=None): + return await self.device.set_connection_phy(self, tx_phys, rx_phys, phy_options) + + async def get_rssi(self): + return await self.device.get_connection_rssi(self) + + async def get_phy(self): + return await self.device.get_connection_phy(self) + # [Classic only] async def request_remote_name(self): return await self.device.request_remote_name(self) + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_value, traceback): + if exc_type is None: + try: + await self.disconnect() + except HCI_StatusError as error: + # Invalid parameter means the connection is no longer valid + if error.error_code != HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR: + raise + def __str__(self): - return f'Connection(handle=0x{self.handle:04X}, role={self.role_name}, address={self.peer_address})' + return ( + f'Connection(handle=0x{self.handle:04X}, ' + f'role={self.role_name}, ' + f'address={self.peer_address})' + ) # ----------------------------------------------------------------------------- class DeviceConfiguration: - def __init__(self): + def __init__(self) -> None: # Setup defaults - self.name = DEVICE_DEFAULT_NAME - self.address = DEVICE_DEFAULT_ADDRESS - self.class_of_device = DEVICE_DEFAULT_CLASS_OF_DEVICE - self.scan_response_data = DEVICE_DEFAULT_SCAN_RESPONSE_DATA + self.name = DEVICE_DEFAULT_NAME + self.address = Address(DEVICE_DEFAULT_ADDRESS) + self.class_of_device = DEVICE_DEFAULT_CLASS_OF_DEVICE + self.scan_response_data = DEVICE_DEFAULT_SCAN_RESPONSE_DATA self.advertising_interval_min = DEVICE_DEFAULT_ADVERTISING_INTERVAL self.advertising_interval_max = DEVICE_DEFAULT_ADVERTISING_INTERVAL - self.le_enabled = True + self.le_enabled = True # LE host enable 2nd parameter - self.le_simultaneous_enabled = True - self.classic_sc_enabled = True - self.classic_ssp_enabled = True + self.le_simultaneous_enabled = False + self.classic_enabled = False + self.classic_sc_enabled = True + self.classic_ssp_enabled = True + self.classic_smp_enabled = True + self.classic_accept_any = True + self.connectable = True + self.discoverable = True self.advertising_data = bytes( - AdvertisingData([(AdvertisingData.COMPLETE_LOCAL_NAME, bytes(self.name, 'utf-8'))]) + AdvertisingData( + [(AdvertisingData.COMPLETE_LOCAL_NAME, bytes(self.name, 'utf-8'))] + ) ) - self.irk = bytes(16) # This really must be changed for any level of security + self.irk = bytes(16) # This really must be changed for any level of security self.keystore = None + self.gatt_services: List[Dict[str, Any]] = [] - def load_from_dict(self, config): + def load_from_dict(self, config: Dict[str, Any]) -> None: # Load simple properties self.name = config.get('name', self.name) - self.address = Address(config.get('address', self.address)) + if address := config.get('address', None): + self.address = Address(address) self.class_of_device = config.get('class_of_device', self.class_of_device) - self.advertising_interval_min = config.get('advertising_interval', self.advertising_interval_min) + self.advertising_interval_min = config.get( + 'advertising_interval', self.advertising_interval_min + ) self.advertising_interval_max = self.advertising_interval_min - self.keystore = config.get('keystore') - self.le_enabled = config.get('le_enabled', self.le_enabled) - self.le_simultaneous_enabled = config.get('le_simultaneous_enabled', self.le_simultaneous_enabled) - self.classic_sc_enabled = config.get('classic_sc_enabled', self.classic_sc_enabled) - self.classic_ssp_enabled = config.get('classic_ssp_enabled', self.classic_ssp_enabled) + self.keystore = config.get('keystore') + self.le_enabled = config.get('le_enabled', self.le_enabled) + self.le_simultaneous_enabled = config.get( + 'le_simultaneous_enabled', self.le_simultaneous_enabled + ) + self.classic_enabled = config.get('classic_enabled', self.classic_enabled) + self.classic_sc_enabled = config.get( + 'classic_sc_enabled', self.classic_sc_enabled + ) + self.classic_ssp_enabled = config.get( + 'classic_ssp_enabled', self.classic_ssp_enabled + ) + self.classic_smp_enabled = config.get( + 'classic_smp_enabled', self.classic_smp_enabled + ) + self.classic_accept_any = config.get( + 'classic_accept_any', self.classic_accept_any + ) + self.connectable = config.get('connectable', self.connectable) + self.discoverable = config.get('discoverable', self.discoverable) + self.gatt_services = config.get('gatt_services', self.gatt_services) # Load or synthesize an IRK irk = config.get('irk') @@ -295,7 +817,8 @@ class DeviceConfiguration: self.irk = bytes.fromhex(irk) else: # Construct an IRK from the address bytes - # NOTE: this is not secure, but will always give the same IRK for the same address + # NOTE: this is not secure, but will always give the same IRK for the same + # address address_bytes = bytes(self.address) self.irk = (address_bytes * 3)[:16] @@ -305,22 +828,25 @@ class DeviceConfiguration: self.advertising_data = bytes.fromhex(advertising_data) def load_from_file(self, filename): - with open(filename, 'r') as file: + with open(filename, 'r', encoding='utf-8') as file: self.load_from_dict(json.load(file)) + # ----------------------------------------------------------------------------- # Decorators used with the following Device class # (we define them outside of the Device class, because defining decorators # within a class requires unnecessarily complicated acrobatics) # ----------------------------------------------------------------------------- + # Decorator that converts the first argument from a connection handle to a connection def with_connection_from_handle(function): @functools.wraps(function) def wrapper(self, connection_handle, *args, **kwargs): if (connection := self.lookup_connection(connection_handle)) is None: - raise ValueError('no connection for handle') + raise ValueError(f"no connection for handle: 0x{connection_handle:04x}") return function(self, connection, *args, **kwargs) + return wrapper @@ -328,10 +854,28 @@ def with_connection_from_handle(function): def with_connection_from_address(function): @functools.wraps(function) def wrapper(self, address, *args, **kwargs): + if connection := self.pending_connections.get(address, False): + return function(self, connection, *args, **kwargs) for connection in self.connections.values(): if connection.peer_address == address: return function(self, connection, *args, **kwargs) raise ValueError('no connection for address') + + return wrapper + + +# Decorator that tries to convert the first argument from a bluetooth address to a +# connection +def try_with_connection_from_address(function): + @functools.wraps(function) + def wrapper(self, address, *args, **kwargs): + if connection := self.pending_connections.get(address, False): + return function(self, connection, address, *args, **kwargs) + for connection in self.connections.values(): + if connection.peer_address == address: + return function(self, connection, address, *args, **kwargs) + return function(self, None, address, *args, **kwargs) + return wrapper @@ -345,15 +889,31 @@ def host_event_handler(function): # List of host event handlers for the Device class. # (we define this list outside the class, because referencing a class in method # decorators is not straightforward) -device_host_event_handlers = [] +device_host_event_handlers: List[str] = [] # ----------------------------------------------------------------------------- class Device(CompositeEventEmitter): + # Incomplete list of fields. + random_address: Address + public_address: Address + classic_enabled: bool + name: str + class_of_device: int + gatt_server: gatt_server.Server + advertising_data: bytes + scan_response_data: bytes + connections: Dict[int, Connection] + pending_connections: Dict[Address, Connection] + classic_pending_accepts: Dict[ + Address, List[asyncio.Future[Union[Connection, Tuple[Address, int, int]]]] + ] + advertisement_accumulators: Dict[Address, AdvertisementDataAccumulator] + config: DeviceConfiguration @composite_listener class Listener: - def on_advertisement(self, address, data, rssi, advertisement_type): + def on_advertisement(self, advertisement): pass def on_inquiry_result(self, address, class_of_device, data, rssi): @@ -365,7 +925,12 @@ class Device(CompositeEventEmitter): def on_connection_failure(self, error): pass - def on_characteristic_subscription(self, connection, characteristic, notify_enabled, indicate_enabled): + def on_connection_request(self, bd_addr, class_of_device, link_type): + pass + + def on_characteristic_subscription( + self, connection, characteristic, notify_enabled, indicate_enabled + ): pass @classmethod @@ -374,8 +939,8 @@ class Device(CompositeEventEmitter): Create a Device instance with a Host configured to communicate with a controller through an HCI source/sink ''' - host = Host(controller_source = hci_source, controller_sink = hci_sink) - return cls(name = name, address = address, host = host) + host = Host(controller_source=hci_source, controller_sink=hci_sink) + return cls(name=name, address=address, host=host) @classmethod def from_config_file(cls, filename): @@ -387,49 +952,100 @@ class Device(CompositeEventEmitter): def from_config_file_with_hci(cls, filename, hci_source, hci_sink): config = DeviceConfiguration() config.load_from_file(filename) - host = Host(controller_source = hci_source, controller_sink = hci_sink) - return cls(config = config, host = host) + host = Host(controller_source=hci_source, controller_sink=hci_sink) + return cls(config=config, host=host) - def __init__(self, name = None, address = None, config = None, host = None, generic_access_service = True): + def __init__( + self, + name: Optional[str] = None, + address: Optional[Address] = None, + config: Optional[DeviceConfiguration] = None, + host: Optional[Host] = None, + generic_access_service: bool = True, + ) -> None: super().__init__() - self._host = None - self.powered_on = False - self.advertising = False + self._host = None + self.powered_on = False + self.advertising = False + self.advertising_type = None + self.auto_restart_inquiry = True self.auto_restart_advertising = False - self.command_timeout = 10 # seconds - self.gatt_server = gatt_server.Server(self) - self.sdp_server = sdp.Server(self) - self.l2cap_channel_manager = l2cap.ChannelManager() - self.advertisement_data = {} - self.scanning = False - self.discovering = False - self.connecting = False - self.disconnecting = False - self.connections = {} # Connections, by connection handle - self.classic_enabled = False - self.discoverable = False - self.connectable = False - self.inquiry_response = None - self.address_resolver = None + self.command_timeout = 10 # seconds + self.gatt_server = gatt_server.Server(self) + self.sdp_server = sdp.Server(self) + self.l2cap_channel_manager = l2cap.ChannelManager( + [l2cap.L2CAP_Information_Request.EXTENDED_FEATURE_FIXED_CHANNELS] + ) + self.advertisement_accumulators = {} # Accumulators, by address + self.scanning = False + self.scanning_is_passive = False + self.discovering = False + self.le_connecting = False + self.disconnecting = False + self.connections = {} # Connections, by connection handle + self.pending_connections = {} # Connections, by BD address (BR/EDR only) + self.classic_enabled = False + self.inquiry_response = None + self.address_resolver = None + self.classic_pending_accepts = { + Address.ANY: [] + } # Futures, by BD address OR [Futures] for Address.ANY + + # Own address type cache + self.advertising_own_address_type = None + self.connect_own_address_type = None # Use the initial config or a default + config = config or DeviceConfiguration() + self.config = config + self.public_address = Address('00:00:00:00:00:00') - if config is None: - config = DeviceConfiguration() - self.name = config.name - self.random_address = config.address - self.class_of_device = config.class_of_device - self.scan_response_data = config.scan_response_data - self.advertising_data = config.advertising_data + self.name = config.name + self.random_address = config.address + self.class_of_device = config.class_of_device + self.scan_response_data = config.scan_response_data + self.advertising_data = config.advertising_data self.advertising_interval_min = config.advertising_interval_min self.advertising_interval_max = config.advertising_interval_max - self.keystore = keys.KeyStore.create_for_device(config) - self.irk = config.irk - self.le_enabled = config.le_enabled - self.le_simultaneous_enabled = config.le_simultaneous_enabled - self.classic_ssp_enabled = config.classic_ssp_enabled - self.classic_sc_enabled = config.classic_sc_enabled + self.keystore = None + self.irk = config.irk + self.le_enabled = config.le_enabled + self.classic_enabled = config.classic_enabled + self.le_simultaneous_enabled = config.le_simultaneous_enabled + self.classic_sc_enabled = config.classic_sc_enabled + self.classic_ssp_enabled = config.classic_ssp_enabled + self.classic_smp_enabled = config.classic_smp_enabled + self.discoverable = config.discoverable + self.connectable = config.connectable + self.classic_accept_any = config.classic_accept_any + + for service in config.gatt_services: + characteristics = [] + for characteristic in service.get("characteristics", []): + descriptors = [] + for descriptor in characteristic.get("descriptors", []): + # Leave this check until 5/25/2023 + if descriptor.get("permission", False): + raise Exception( + "Error parsing Device Config's GATT Services. The key 'permission' must be renamed to 'permissions'" + ) + new_descriptor = Descriptor( + attribute_type=descriptor["descriptor_type"], + permissions=descriptor["permissions"], + ) + descriptors.append(new_descriptor) + new_characteristic = Characteristic( + uuid=characteristic["uuid"], + properties=Characteristic.Properties.from_string( + characteristic["properties"] + ), + permissions=characteristic["permissions"], + descriptors=descriptors, + ) + characteristics.append(new_characteristic) + new_service = Service(uuid=service["uuid"], characteristics=characteristics) + self.gatt_server.add_service(new_service) # If a name is passed, override the name from the config if name: @@ -437,29 +1053,33 @@ class Device(CompositeEventEmitter): # If an address is passed, override the address from the config if address: - if type(address) is str: + if isinstance(address, str): address = Address(address) self.random_address = address # Setup SMP - # TODO: allow using a public address - self.smp_manager = smp.Manager(self, self.random_address) + self.smp_manager = smp.Manager( + self, pairing_config_factory=lambda connection: PairingConfig() + ) + + self.l2cap_channel_manager.register_fixed_channel(smp.SMP_CID, self.on_smp_pdu) # Register the SDP server with the L2CAP Channel Manager self.sdp_server.register(self.l2cap_channel_manager) - # Add a GAP Service if requested - if generic_access_service: - self.gatt_server.add_service(GenericAccessService(self.name)) + self.add_default_services(generic_access_service) + self.l2cap_channel_manager.register_fixed_channel(ATT_CID, self.on_gatt_pdu) # Forward some events setup_event_forwarding(self.gatt_server, self, 'characteristic_subscription') # Set the initial host - self.host = host + if host: + self.host = host @property - def host(self): + def host(self) -> Host: + assert self._host return self._host @host.setter @@ -467,7 +1087,9 @@ class Device(CompositeEventEmitter): # Unsubscribe from events from the current host if self._host: for event_name in device_host_event_handlers: - self._host.remove_listener(event_name, getattr(self, f'on_{event_name}')) + self._host.remove_listener( + event_name, getattr(self, f'on_{event_name}') + ) # Subscribe to events from the new host if host: @@ -475,13 +1097,13 @@ class Device(CompositeEventEmitter): host.on(event_name, getattr(self, f'on_{event_name}')) # Update the references to the new host - self._host = host + self._host = host self.l2cap_channel_manager.host = host # Set providers for the new host if host: host.long_term_key_provider = self.get_long_term_key - host.link_key_provider = self.get_link_key + host.link_key_provider = self.get_link_key @property def sdp_service_records(self): @@ -491,18 +1113,29 @@ class Device(CompositeEventEmitter): def sdp_service_records(self, service_records): self.sdp_server.service_records = service_records - def lookup_connection(self, connection_handle): + def lookup_connection(self, connection_handle: int) -> Optional[Connection]: if connection := self.connections.get(connection_handle): return connection - def find_connection_by_bd_addr(self, bd_addr, transport=None): + return None + + def find_connection_by_bd_addr( + self, + bd_addr: Address, + transport: Optional[int] = None, + check_address_type: bool = False, + ) -> Optional[Connection]: for connection in self.connections.values(): - if connection.peer_address.get_bytes() == bd_addr.get_bytes(): + if connection.peer_address.to_bytes() == bd_addr.to_bytes(): + if ( + check_address_type + and connection.peer_address.address_type != bd_addr.address_type + ): + continue if transport is None or connection.transport == transport: return connection - def register_l2cap_server(self, psm, server): - self.l2cap_channel_manager.register_server(psm, server) + return None def create_l2cap_connector(self, connection, psm): return lambda: self.l2cap_channel_manager.connect(connection, psm) @@ -510,53 +1143,130 @@ class Device(CompositeEventEmitter): def create_l2cap_registrar(self, psm): return lambda handler: self.register_l2cap_server(psm, handler) + def register_l2cap_server(self, psm, server): + self.l2cap_channel_manager.register_server(psm, server) + + def register_l2cap_channel_server( + self, + psm, + server, + max_credits=DEVICE_DEFAULT_L2CAP_COC_MAX_CREDITS, + mtu=DEVICE_DEFAULT_L2CAP_COC_MTU, + mps=DEVICE_DEFAULT_L2CAP_COC_MPS, + ): + return self.l2cap_channel_manager.register_le_coc_server( + psm, server, max_credits, mtu, mps + ) + + async def open_l2cap_channel( + self, + connection, + psm, + max_credits=DEVICE_DEFAULT_L2CAP_COC_MAX_CREDITS, + mtu=DEVICE_DEFAULT_L2CAP_COC_MTU, + mps=DEVICE_DEFAULT_L2CAP_COC_MPS, + ): + return await self.l2cap_channel_manager.open_le_coc( + connection, psm, max_credits, mtu, mps + ) + def send_l2cap_pdu(self, connection_handle, cid, pdu): self.host.send_l2cap_pdu(connection_handle, cid, pdu) - async def send_command(self, command): + async def send_command(self, command, check_result=False): try: - return await asyncio.wait_for(self.host.send_command(command), self.command_timeout) - except asyncio.TimeoutError: + return await asyncio.wait_for( + self.host.send_command(command, check_result), self.command_timeout + ) + except asyncio.TimeoutError as error: logger.warning('!!! Command timed out') + raise CommandTimeoutError() from error - async def power_on(self): + async def power_on(self) -> None: # Reset the controller await self.host.reset() - response = await self.send_command(HCI_Read_BD_ADDR_Command()) + # Try to get the public address from the controller + response = await self.send_command(HCI_Read_BD_ADDR_Command()) # type: ignore[call-arg] if response.return_parameters.status == HCI_SUCCESS: - logger.debug(color(f'BD_ADDR: {response.return_parameters.bd_addr}', 'yellow')) + logger.debug( + color(f'BD_ADDR: {response.return_parameters.bd_addr}', 'yellow') + ) self.public_address = response.return_parameters.bd_addr + # Instantiate the Key Store (we do this here rather than at __init__ time + # because some Key Store implementations use the public address as a namespace) + if self.keystore is None: + self.keystore = KeyStore.create_for_device(self) + + # Finish setting up SMP based on post-init configurable options + if self.classic_smp_enabled: + self.l2cap_channel_manager.register_fixed_channel( + smp.SMP_BR_CID, self.on_smp_pdu + ) + + if self.host.supports_command(HCI_WRITE_LE_HOST_SUPPORT_COMMAND): + await self.send_command( + HCI_Write_LE_Host_Support_Command( + le_supported_host=int(self.le_enabled), + simultaneous_le_host=int(self.le_simultaneous_enabled), + ) # type: ignore[call-arg] + ) - await self.send_command(HCI_Write_LE_Host_Support_Command( - le_supported_host = int(self.le_enabled), - simultaneous_le_host = int(self.le_simultaneous_enabled), - )) if self.le_enabled: # Set the controller address - await self.send_command(HCI_LE_Set_Random_Address_Command( - random_address = self.random_address - )) + if self.random_address == Address.ANY_RANDOM: + # Try to use an address generated at random by the controller + if self.host.supports_command(HCI_LE_RAND_COMMAND): + # Get 8 random bytes + response = await self.send_command( + HCI_LE_Rand_Command(), check_result=True # type: ignore[call-arg] + ) + + # Ensure the address bytes can be a static random address + address_bytes = response.return_parameters.random_number[ + :5 + ] + bytes([response.return_parameters.random_number[5] | 0xC0]) + + # Create a static random address from the random bytes + self.random_address = Address(address_bytes) + + if self.random_address != Address.ANY_RANDOM: + logger.debug( + color( + f'LE Random Address: {self.random_address}', + 'yellow', + ) + ) + await self.send_command( + HCI_LE_Set_Random_Address_Command( + random_address=self.random_address + ), # type: ignore[call-arg] + check_result=True, + ) # Load the address resolving list - if self.keystore: - await self.send_command(HCI_LE_Clear_Resolving_List_Command()) + if self.keystore and self.host.supports_command( + HCI_LE_CLEAR_RESOLVING_LIST_COMMAND + ): + await self.send_command(HCI_LE_Clear_Resolving_List_Command()) # type: ignore[call-arg] resolving_keys = await self.keystore.get_resolving_keys() - for (irk, address) in resolving_keys: + for irk, address in resolving_keys: await self.send_command( HCI_LE_Add_Device_To_Resolving_List_Command( - peer_identity_address_type = address.address_type, - peer_identity_address = address, - peer_irk = irk, - local_irk = self.irk - ) + peer_identity_address_type=address.address_type, + peer_identity_address=address, + peer_irk=irk, + local_irk=self.irk, + ) # type: ignore[call-arg] ) # Enable address resolution # await self.send_command( - # HCI_LE_Set_Address_Resolution_Enable_Command(address_resolution_enable=1) + # HCI_LE_Set_Address_Resolution_Enable_Command( + # address_resolution_enable=1) + # ) # ) # Create a host-side address resolver @@ -564,72 +1274,126 @@ class Device(CompositeEventEmitter): if self.classic_enabled: await self.send_command( - HCI_Write_Local_Name_Command(local_name=self.name.encode('utf8')) + HCI_Write_Local_Name_Command(local_name=self.name.encode('utf8')) # type: ignore[call-arg] ) await self.send_command( - HCI_Write_Class_Of_Device_Command(class_of_device = self.class_of_device) + HCI_Write_Class_Of_Device_Command(class_of_device=self.class_of_device) # type: ignore[call-arg] ) await self.send_command( HCI_Write_Simple_Pairing_Mode_Command( - simple_pairing_mode=int(self.classic_ssp_enabled)) + simple_pairing_mode=int(self.classic_ssp_enabled) + ) # type: ignore[call-arg] ) await self.send_command( HCI_Write_Secure_Connections_Host_Support_Command( - secure_connections_host_support=int(self.classic_sc_enabled)) + secure_connections_host_support=int(self.classic_sc_enabled) + ) # type: ignore[call-arg] ) - - # Let the SMP manager know about the address - # TODO: allow using a public address - self.smp_manager.address = self.random_address + await self.set_connectable(self.connectable) + await self.set_discoverable(self.discoverable) # Done self.powered_on = True - async def start_advertising(self, auto_restart=False): - self.auto_restart_advertising = auto_restart + async def power_off(self) -> None: + if self.powered_on: + await self.host.flush() + self.powered_on = False + + def supports_le_feature(self, feature): + return self.host.supports_le_feature(feature) + + def supports_le_phy(self, phy): + if phy == HCI_LE_1M_PHY: + return True + + feature_map = { + HCI_LE_2M_PHY: HCI_LE_2M_PHY_LE_SUPPORTED_FEATURE, + HCI_LE_CODED_PHY: HCI_LE_CODED_PHY_LE_SUPPORTED_FEATURE, + } + if phy not in feature_map: + raise ValueError('invalid PHY') + + return self.host.supports_le_feature(feature_map[phy]) + async def start_advertising( + self, + advertising_type: AdvertisingType = AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE, + target: Optional[Address] = None, + own_address_type: int = OwnAddressType.RANDOM, + auto_restart: bool = False, + ) -> None: # If we're advertising, stop first if self.advertising: await self.stop_advertising() - # Set/update the advertising data - await self.send_command(HCI_LE_Set_Advertising_Data_Command( - advertising_data = self.advertising_data - )) + # Set/update the advertising data if the advertising type allows it + if advertising_type.has_data: + await self.send_command( + HCI_LE_Set_Advertising_Data_Command( + advertising_data=self.advertising_data + ), # type: ignore[call-arg] + check_result=True, + ) + + # Set/update the scan response data if the advertising is scannable + if advertising_type.is_scannable: + await self.send_command( + HCI_LE_Set_Scan_Response_Data_Command( + scan_response_data=self.scan_response_data + ), # type: ignore[call-arg] + check_result=True, + ) - # Set/update the scan response data - await self.send_command(HCI_LE_Set_Scan_Response_Data_Command( - scan_response_data = self.scan_response_data - )) + # Decide what peer address to use + if advertising_type.is_directed: + if target is None: + raise ValueError('directed advertising requires a target address') + + peer_address = target + peer_address_type = target.address_type + else: + peer_address = Address('00:00:00:00:00:00') + peer_address_type = Address.PUBLIC_DEVICE_ADDRESS # Set the advertising parameters - await self.send_command(HCI_LE_Set_Advertising_Parameters_Command( - # TODO: use real values, not fixed ones - advertising_interval_min = self.advertising_interval_min, - advertising_interval_max = self.advertising_interval_max, - advertising_type = HCI_LE_Set_Advertising_Parameters_Command.ADV_IND, - own_address_type = Address.RANDOM_DEVICE_ADDRESS, # TODO: allow using the public address - peer_address_type = Address.PUBLIC_DEVICE_ADDRESS, - peer_address = Address('00:00:00:00:00:00'), - advertising_channel_map = 7, - advertising_filter_policy = 0 - )) + await self.send_command( + HCI_LE_Set_Advertising_Parameters_Command( + advertising_interval_min=self.advertising_interval_min, + advertising_interval_max=self.advertising_interval_max, + advertising_type=int(advertising_type), + own_address_type=own_address_type, + peer_address_type=peer_address_type, + peer_address=peer_address, + advertising_channel_map=7, + advertising_filter_policy=0, + ), # type: ignore[call-arg] + check_result=True, + ) # Enable advertising - await self.send_command(HCI_LE_Set_Advertising_Enable_Command( - advertising_enable = 1 - )) + await self.send_command( + HCI_LE_Set_Advertising_Enable_Command(advertising_enable=1), # type: ignore[call-arg] + check_result=True, + ) + self.advertising_own_address_type = own_address_type + self.auto_restart_advertising = auto_restart + self.advertising_type = advertising_type self.advertising = True - async def stop_advertising(self): + async def stop_advertising(self) -> None: # Disable advertising if self.advertising: - await self.send_command(HCI_LE_Set_Advertising_Enable_Command( - advertising_enable = 0 - )) + await self.send_command( + HCI_LE_Set_Advertising_Enable_Command(advertising_enable=0), # type: ignore[call-arg] + check_result=True, + ) + self.advertising_own_address_type = None self.advertising = False + self.advertising_type = None + self.auto_restart_advertising = False @property def is_advertising(self): @@ -637,42 +1401,122 @@ class Device(CompositeEventEmitter): async def start_scanning( self, - active=True, - scan_interval=DEVICE_DEFAULT_SCAN_INTERVAL, # Scan interval in ms - scan_window=DEVICE_DEFAULT_SCAN_WINDOW, # Scan window in ms - own_address_type=Address.RANDOM_DEVICE_ADDRESS, - filter_duplicates=False - ): + legacy: bool = False, + active: bool = True, + scan_interval: int = DEVICE_DEFAULT_SCAN_INTERVAL, # Scan interval in ms + scan_window: int = DEVICE_DEFAULT_SCAN_WINDOW, # Scan window in ms + own_address_type: int = OwnAddressType.RANDOM, + filter_duplicates: bool = False, + scanning_phys: Tuple[int, int] = (HCI_LE_1M_PHY, HCI_LE_CODED_PHY), + ) -> None: # Check that the arguments are legal if scan_interval < scan_window: raise ValueError('scan_interval must be >= scan_window') - if scan_interval < DEVICE_MIN_SCAN_INTERVAL or scan_interval > DEVICE_MAX_SCAN_INTERVAL: + if ( + scan_interval < DEVICE_MIN_SCAN_INTERVAL + or scan_interval > DEVICE_MAX_SCAN_INTERVAL + ): raise ValueError('scan_interval out of range') if scan_window < DEVICE_MIN_SCAN_WINDOW or scan_window > DEVICE_MAX_SCAN_WINDOW: raise ValueError('scan_interval out of range') - # Set the scanning parameters - scan_type = HCI_LE_Set_Scan_Parameters_Command.ACTIVE_SCANNING if active else HCI_LE_Set_Scan_Parameters_Command.PASSIVE_SCANNING - await self.send_command(HCI_LE_Set_Scan_Parameters_Command( - le_scan_type = scan_type, - le_scan_interval = int(scan_window / 0.625), - le_scan_window = int(scan_window / 0.625), - own_address_type = own_address_type, - scanning_filter_policy = HCI_LE_Set_Scan_Parameters_Command.BASIC_UNFILTERED_POLICY - )) + # Reset the accumulators + self.advertisement_accumulators = {} # Enable scanning - await self.send_command(HCI_LE_Set_Scan_Enable_Command( - le_scan_enable = 1, - filter_duplicates = 1 if filter_duplicates else 0 - )) + if not legacy and self.supports_le_feature( + HCI_LE_EXTENDED_ADVERTISING_LE_SUPPORTED_FEATURE + ): + # Set the scanning parameters + scan_type = ( + HCI_LE_Set_Extended_Scan_Parameters_Command.ACTIVE_SCANNING + if active + else HCI_LE_Set_Extended_Scan_Parameters_Command.PASSIVE_SCANNING + ) + scanning_filter_policy = ( + HCI_LE_Set_Extended_Scan_Parameters_Command.BASIC_UNFILTERED_POLICY + ) # TODO: support other types + + scanning_phy_count = 0 + scanning_phys_bits = 0 + if HCI_LE_1M_PHY in scanning_phys: + scanning_phys_bits |= 1 << HCI_LE_1M_PHY_BIT + scanning_phy_count += 1 + if HCI_LE_CODED_PHY in scanning_phys: + if self.supports_le_feature(HCI_LE_CODED_PHY_LE_SUPPORTED_FEATURE): + scanning_phys_bits |= 1 << HCI_LE_CODED_PHY_BIT + scanning_phy_count += 1 + + if scanning_phy_count == 0: + raise ValueError('at least one scanning PHY must be enabled') + + await self.send_command( + HCI_LE_Set_Extended_Scan_Parameters_Command( + own_address_type=own_address_type, + scanning_filter_policy=scanning_filter_policy, + scanning_phys=scanning_phys_bits, + scan_types=[scan_type] * scanning_phy_count, + scan_intervals=[int(scan_window / 0.625)] * scanning_phy_count, + scan_windows=[int(scan_window / 0.625)] * scanning_phy_count, + ), # type: ignore[call-arg] + check_result=True, + ) + + # Enable scanning + await self.send_command( + HCI_LE_Set_Extended_Scan_Enable_Command( + enable=1, + filter_duplicates=1 if filter_duplicates else 0, + duration=0, # TODO allow other values + period=0, # TODO allow other values + ), # type: ignore[call-arg] + check_result=True, + ) + else: + # Set the scanning parameters + scan_type = ( + HCI_LE_Set_Scan_Parameters_Command.ACTIVE_SCANNING + if active + else HCI_LE_Set_Scan_Parameters_Command.PASSIVE_SCANNING + ) + await self.send_command( + # pylint: disable=line-too-long + HCI_LE_Set_Scan_Parameters_Command( + le_scan_type=scan_type, + le_scan_interval=int(scan_window / 0.625), + le_scan_window=int(scan_window / 0.625), + own_address_type=own_address_type, + scanning_filter_policy=HCI_LE_Set_Scan_Parameters_Command.BASIC_UNFILTERED_POLICY, + ), # type: ignore[call-arg] + check_result=True, + ) + + # Enable scanning + await self.send_command( + HCI_LE_Set_Scan_Enable_Command( + le_scan_enable=1, filter_duplicates=1 if filter_duplicates else 0 + ), # type: ignore[call-arg] + check_result=True, + ) + + self.scanning_is_passive = not active self.scanning = True - async def stop_scanning(self): - await self.send_command(HCI_LE_Set_Scan_Enable_Command( - le_scan_enable = 0, - filter_duplicates = 0 - )) + async def stop_scanning(self) -> None: + # Disable scanning + if self.supports_le_feature(HCI_LE_EXTENDED_ADVERTISING_LE_SUPPORTED_FEATURE): + await self.send_command( + HCI_LE_Set_Extended_Scan_Enable_Command( + enable=0, filter_duplicates=0, duration=0, period=0 + ), # type: ignore[call-arg] + check_result=True, + ) + else: + await self.send_command( + HCI_LE_Set_Scan_Enable_Command(le_scan_enable=0, filter_duplicates=0), # type: ignore[call-arg] + check_result=True, + ) + self.scanning = False @property @@ -680,36 +1524,37 @@ class Device(CompositeEventEmitter): return self.scanning @host_event_handler - def on_advertising_report(self, address, data, rssi, advertisement_type): - if not (accumulator := self.advertisement_data.get(address)): - accumulator = AdvertisementDataAccumulator() - self.advertisement_data[address] = accumulator - accumulator.update(data, advertisement_type) - if accumulator.flushable: - self.emit( - 'advertisement', - address, - accumulator.advertising_data, - rssi, - accumulator.connectable - ) - - async def start_discovery(self): - await self.host.send_command(HCI_Write_Inquiry_Mode_Command(inquiry_mode=HCI_EXTENDED_INQUIRY_MODE)) + def on_advertising_report(self, report): + if not (accumulator := self.advertisement_accumulators.get(report.address)): + accumulator = AdvertisementDataAccumulator(passive=self.scanning_is_passive) + self.advertisement_accumulators[report.address] = accumulator + if advertisement := accumulator.update(report): + self.emit('advertisement', advertisement) + + async def start_discovery(self, auto_restart: bool = True) -> None: + await self.send_command( + HCI_Write_Inquiry_Mode_Command(inquiry_mode=HCI_EXTENDED_INQUIRY_MODE), # type: ignore[call-arg] + check_result=True, + ) - response = await self.send_command(HCI_Inquiry_Command( - lap = HCI_GENERAL_INQUIRY_LAP, - inquiry_length = DEVICE_DEFAULT_INQUIRY_LENGTH, - num_responses = 0 # Unlimited number of responses. - )) + response = await self.send_command( + HCI_Inquiry_Command( + lap=HCI_GENERAL_INQUIRY_LAP, + inquiry_length=DEVICE_DEFAULT_INQUIRY_LENGTH, + num_responses=0, # Unlimited number of responses. + ) # type: ignore[call-arg] + ) if response.status != HCI_Command_Status_Event.PENDING: self.discovering = False - raise RuntimeError(f'HCI_Inquiry command failed: {HCI_Constant.status_name(response.status)} ({response.status})') + raise HCI_StatusError(response) + self.auto_restart_inquiry = auto_restart self.discovering = True - async def stop_discovery(self): - await self.send_command(HCI_Inquiry_Cancel_Command()) + async def stop_discovery(self) -> None: + if self.discovering: + await self.send_command(HCI_Inquiry_Cancel_Command(), check_result=True) # type: ignore[call-arg] + self.auto_restart_inquiry = True self.discovering = False @host_event_handler @@ -719,7 +1564,7 @@ class Device(CompositeEventEmitter): address, class_of_device, AdvertisingData.from_bytes(data), - rssi + rssi, ) async def set_scan_enable(self, inquiry_scan_enabled, page_scan_enabled): @@ -732,45 +1577,73 @@ class Device(CompositeEventEmitter): else: scan_enable = 0x00 - return await self.send_command(HCI_Write_Scan_Enable_Command(scan_enable = scan_enable)) + return await self.send_command( + HCI_Write_Scan_Enable_Command(scan_enable=scan_enable) + ) - async def set_discoverable(self, discoverable=True): + async def set_discoverable(self, discoverable: bool = True) -> None: self.discoverable = discoverable if self.classic_enabled: # Synthesize an inquiry response if none is set already if self.inquiry_response is None: self.inquiry_response = bytes( - AdvertisingData([ - (AdvertisingData.COMPLETE_LOCAL_NAME, bytes(self.name, 'utf-8')) - ]) + AdvertisingData( + [ + ( + AdvertisingData.COMPLETE_LOCAL_NAME, + bytes(self.name, 'utf-8'), + ) + ] + ) ) # Update the controller - await self.host.send_command( + await self.send_command( HCI_Write_Extended_Inquiry_Response_Command( - fec_required = 0, - extended_inquiry_response = self.inquiry_response - ) + fec_required=0, extended_inquiry_response=self.inquiry_response + ), # type: ignore[call-arg] + check_result=True, ) await self.set_scan_enable( - inquiry_scan_enabled = self.discoverable, - page_scan_enabled = self.connectable + inquiry_scan_enabled=self.discoverable, + page_scan_enabled=self.connectable, ) - async def set_connectable(self, connectable=True): + async def set_connectable(self, connectable: bool = True) -> None: self.connectable = connectable if self.classic_enabled: await self.set_scan_enable( - inquiry_scan_enabled = self.discoverable, - page_scan_enabled = self.connectable + inquiry_scan_enabled=self.discoverable, + page_scan_enabled=self.connectable, ) - async def connect(self, peer_address, transport=BT_LE_TRANSPORT): + async def connect( + self, + peer_address: Union[Address, str], + transport: int = BT_LE_TRANSPORT, + connection_parameters_preferences: Optional[ + Dict[int, ConnectionParametersPreferences] + ] = None, + own_address_type: int = OwnAddressType.RANDOM, + timeout: Optional[float] = DEVICE_DEFAULT_CONNECT_TIMEOUT, + ) -> Connection: ''' Request a connection to a peer. - This method cannot be called if there is already a pending connection. + When transport is BLE, this method cannot be called if there is already a + pending connection. + + connection_parameters_preferences: (BLE only, ignored for BR/EDR) + * None: use the 1M PHY with default parameters + * map: each entry has a PHY as key and a ConnectionParametersPreferences + object as value + + own_address_type: (BLE only) ''' + # Check parameters + if transport not in (BT_LE_TRANSPORT, BT_BR_EDR_TRANSPORT): + raise ValueError('invalid transport') + # Adjust the transport automatically if we need to if transport == BT_LE_TRANSPORT and not self.le_enabled: transport = BT_BR_EDR_TRANSPORT @@ -778,74 +1651,383 @@ class Device(CompositeEventEmitter): transport = BT_LE_TRANSPORT # Check that there isn't already a pending connection - if self.is_connecting: + if transport == BT_LE_TRANSPORT and self.is_le_connecting: raise InvalidStateError('connection already pending') - if type(peer_address) is str: + if isinstance(peer_address, str): try: - peer_address = Address(peer_address) + peer_address = Address.from_string_for_transport( + peer_address, transport + ) except ValueError: - # If the address is not parssable, assume it is a name instead + # If the address is not parsable, assume it is a name instead logger.debug('looking for peer by name') - peer_address = await self.find_peer_by_name(peer_address, transport) + peer_address = await self.find_peer_by_name( + peer_address, transport + ) # TODO: timeout + else: + # All BR/EDR addresses should be public addresses + if ( + transport == BT_BR_EDR_TRANSPORT + and peer_address.address_type != Address.PUBLIC_DEVICE_ADDRESS + ): + raise ValueError('BR/EDR addresses must be PUBLIC') + + assert isinstance(peer_address, Address) + + def on_connection(connection): + if transport == BT_LE_TRANSPORT or ( + # match BR/EDR connection event against peer address + connection.transport == transport + and connection.peer_address == peer_address + ): + pending_connection.set_result(connection) + + def on_connection_failure(error): + if transport == BT_LE_TRANSPORT or ( + # match BR/EDR connection failure event against peer address + error.transport == transport + and error.peer_address == peer_address + ): + pending_connection.set_exception(error) # Create a future so that we can wait for the connection's result pending_connection = asyncio.get_running_loop().create_future() - self.on('connection', pending_connection.set_result) - self.on('connection_failure', pending_connection.set_exception) - - # Tell the controller to connect - if transport == BT_LE_TRANSPORT: - # TODO: use real values, not fixed ones - result = await self.send_command(HCI_LE_Create_Connection_Command( - le_scan_interval = 96, - le_scan_window = 96, - initiator_filter_policy = 0, - peer_address_type = peer_address.address_type, - peer_address = peer_address, - own_address_type = Address.RANDOM_DEVICE_ADDRESS, - conn_interval_min = 12, - conn_interval_max = 24, - conn_latency = 0, - supervision_timeout = 72, - minimum_ce_length = 0, - maximum_ce_length = 0 - )) - else: - # TODO: use real values, not fixed ones - result = await self.send_command(HCI_Create_Connection_Command( - bd_addr = peer_address, - packet_type = 0xCC18, # FIXME: change - page_scan_repetition_mode = HCI_R2_PAGE_SCAN_REPETITION_MODE, - clock_offset = 0x0000, - allow_role_switch = 0x01, - reserved = 0 - )) + self.on('connection', on_connection) + self.on('connection_failure', on_connection_failure) try: + # Tell the controller to connect + if transport == BT_LE_TRANSPORT: + if connection_parameters_preferences is None: + if connection_parameters_preferences is None: + connection_parameters_preferences = { + HCI_LE_1M_PHY: ConnectionParametersPreferences.default + } + + self.connect_own_address_type = own_address_type + + if self.host.supports_command( + HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND + ): + # Only keep supported PHYs + phys = sorted( + list( + set( + filter( + self.supports_le_phy, + connection_parameters_preferences.keys(), + ) + ) + ) + ) + if not phys: + raise ValueError('at least one supported PHY needed') + + phy_count = len(phys) + initiating_phys = phy_list_to_bits(phys) + + connection_interval_mins = [ + int( + connection_parameters_preferences[ + phy + ].connection_interval_min + / 1.25 + ) + for phy in phys + ] + connection_interval_maxs = [ + int( + connection_parameters_preferences[ + phy + ].connection_interval_max + / 1.25 + ) + for phy in phys + ] + max_latencies = [ + connection_parameters_preferences[phy].max_latency + for phy in phys + ] + supervision_timeouts = [ + int( + connection_parameters_preferences[phy].supervision_timeout + / 10 + ) + for phy in phys + ] + min_ce_lengths = [ + int( + connection_parameters_preferences[phy].min_ce_length / 0.625 + ) + for phy in phys + ] + max_ce_lengths = [ + int( + connection_parameters_preferences[phy].max_ce_length / 0.625 + ) + for phy in phys + ] + + result = await self.send_command( + HCI_LE_Extended_Create_Connection_Command( + initiator_filter_policy=0, + own_address_type=own_address_type, + peer_address_type=peer_address.address_type, + peer_address=peer_address, + initiating_phys=initiating_phys, + scan_intervals=( + int(DEVICE_DEFAULT_CONNECT_SCAN_INTERVAL / 0.625), + ) + * phy_count, + scan_windows=( + int(DEVICE_DEFAULT_CONNECT_SCAN_WINDOW / 0.625), + ) + * phy_count, + connection_interval_mins=connection_interval_mins, + connection_interval_maxs=connection_interval_maxs, + max_latencies=max_latencies, + supervision_timeouts=supervision_timeouts, + min_ce_lengths=min_ce_lengths, + max_ce_lengths=max_ce_lengths, + ) # type: ignore[call-arg] + ) + else: + if HCI_LE_1M_PHY not in connection_parameters_preferences: + raise ValueError('1M PHY preferences required') + + prefs = connection_parameters_preferences[HCI_LE_1M_PHY] + result = await self.send_command( + HCI_LE_Create_Connection_Command( + le_scan_interval=int( + DEVICE_DEFAULT_CONNECT_SCAN_INTERVAL / 0.625 + ), + le_scan_window=int( + DEVICE_DEFAULT_CONNECT_SCAN_WINDOW / 0.625 + ), + initiator_filter_policy=0, + peer_address_type=peer_address.address_type, + peer_address=peer_address, + own_address_type=own_address_type, + connection_interval_min=int( + prefs.connection_interval_min / 1.25 + ), + connection_interval_max=int( + prefs.connection_interval_max / 1.25 + ), + max_latency=prefs.max_latency, + supervision_timeout=int(prefs.supervision_timeout / 10), + min_ce_length=int(prefs.min_ce_length / 0.625), + max_ce_length=int(prefs.max_ce_length / 0.625), + ) # type: ignore[call-arg] + ) + else: + # Save pending connection + self.pending_connections[peer_address] = Connection.incomplete( + self, peer_address, BT_CENTRAL_ROLE + ) + + # TODO: allow passing other settings + result = await self.send_command( + HCI_Create_Connection_Command( + bd_addr=peer_address, + packet_type=0xCC18, # FIXME: change + page_scan_repetition_mode=HCI_R2_PAGE_SCAN_REPETITION_MODE, + clock_offset=0x0000, + allow_role_switch=0x01, + reserved=0, + ) # type: ignore[call-arg] + ) + if result.status != HCI_Command_Status_Event.PENDING: - raise RuntimeError(f'HCI_LE_Create_Connection_Command failed: {HCI_Constant.status_name(result.status)} ({result.status})') + raise HCI_StatusError(result) # Wait for the connection process to complete - self.connecting = True - return await pending_connection + if transport == BT_LE_TRANSPORT: + self.le_connecting = True + + if timeout is None: + return await self.abort_on('flush', pending_connection) + + try: + return await asyncio.wait_for( + asyncio.shield(pending_connection), timeout + ) + except asyncio.TimeoutError: + if transport == BT_LE_TRANSPORT: + await self.send_command(HCI_LE_Create_Connection_Cancel_Command()) # type: ignore[call-arg] + else: + await self.send_command( + HCI_Create_Connection_Cancel_Command(bd_addr=peer_address) # type: ignore[call-arg] + ) + + try: + return await self.abort_on('flush', pending_connection) + except core.ConnectionError as error: + raise core.TimeoutError() from error finally: - self.remove_listener('connection', pending_connection.set_result) - self.remove_listener('connection_failure', pending_connection.set_exception) - self.connecting = False + self.remove_listener('connection', on_connection) + self.remove_listener('connection_failure', on_connection_failure) + if transport == BT_LE_TRANSPORT: + self.le_connecting = False + self.connect_own_address_type = None + else: + self.pending_connections.pop(peer_address, None) + + async def accept( + self, + peer_address: Union[Address, str] = Address.ANY, + role: int = BT_PERIPHERAL_ROLE, + timeout: Optional[float] = DEVICE_DEFAULT_CONNECT_TIMEOUT, + ) -> Connection: + ''' + Wait and accept any incoming connection or a connection from `peer_address` when + set. + + Notes: + * A `connect` to the same peer will not complete this call. + * The `timeout` parameter is only handled while waiting for the connection + request, once received and accepted, the controller shall issue a connection + complete event. + ''' + + if isinstance(peer_address, str): + try: + peer_address = Address(peer_address) + except ValueError: + # If the address is not parsable, assume it is a name instead + logger.debug('looking for peer by name') + peer_address = await self.find_peer_by_name( + peer_address, BT_BR_EDR_TRANSPORT + ) # TODO: timeout + + assert isinstance(peer_address, Address) + + if peer_address == Address.NIL: + raise ValueError('accept on nil address') + + # Create a future so that we can wait for the request + pending_request_fut = asyncio.get_running_loop().create_future() + + if peer_address == Address.ANY: + self.classic_pending_accepts[Address.ANY].append(pending_request_fut) + elif peer_address in self.classic_pending_accepts: + raise InvalidStateError('accept connection already pending') + else: + self.classic_pending_accepts[peer_address] = [pending_request_fut] + + try: + # Wait for a request or a completed connection + pending_request = self.abort_on('flush', pending_request_fut) + result = await ( + asyncio.wait_for(pending_request, timeout) + if timeout + else pending_request + ) + except Exception: + # Remove future from device context + if peer_address == Address.ANY: + self.classic_pending_accepts[Address.ANY].remove(pending_request_fut) + else: + self.classic_pending_accepts.pop(peer_address) + raise + + # Result may already be a completed connection, + # see `on_connection` for details + if isinstance(result, Connection): + return result + + # Otherwise, result came from `on_connection_request` + peer_address, _class_of_device, _link_type = result + assert isinstance(peer_address, Address) + + # Create a future so that we can wait for the connection's result + pending_connection = asyncio.get_running_loop().create_future() + + def on_connection(connection): + if ( + connection.transport == BT_BR_EDR_TRANSPORT + and connection.peer_address == peer_address + ): + pending_connection.set_result(connection) + + def on_connection_failure(error): + if ( + error.transport == BT_BR_EDR_TRANSPORT + and error.peer_address == peer_address + ): + pending_connection.set_exception(error) + + self.on('connection', on_connection) + self.on('connection_failure', on_connection_failure) + + # Save pending connection, with the Peripheral role. + # Even if we requested a role switch in the HCI_Accept_Connection_Request + # command, this connection is still considered Peripheral until an eventual + # role change event. + self.pending_connections[peer_address] = Connection.incomplete( + self, peer_address, BT_PERIPHERAL_ROLE + ) + + try: + # Accept connection request + await self.send_command( + HCI_Accept_Connection_Request_Command(bd_addr=peer_address, role=role) # type: ignore[call-arg] + ) + + # Wait for connection complete + return await self.abort_on('flush', pending_connection) + + finally: + self.remove_listener('connection', on_connection) + self.remove_listener('connection_failure', on_connection_failure) + self.pending_connections.pop(peer_address, None) + + @asynccontextmanager + async def connect_as_gatt(self, peer_address): + async with AsyncExitStack() as stack: + connection = await stack.enter_async_context( + await self.connect(peer_address) + ) + peer = await stack.enter_async_context(Peer(connection)) + + yield peer @property - def is_connecting(self): - return self.connecting + def is_le_connecting(self): + return self.le_connecting @property def is_disconnecting(self): return self.disconnecting - async def cancel_connection(self): - if not self.is_connecting: - return - await self.send_command(HCI_LE_Create_Connection_Cancel_Command()) + async def cancel_connection(self, peer_address=None): + # Low-energy: cancel ongoing connection + if peer_address is None: + if not self.is_le_connecting: + return + await self.send_command( + HCI_LE_Create_Connection_Cancel_Command(), check_result=True + ) + + # BR/EDR: try to cancel to ongoing connection + # NOTE: This API does not prevent from trying to cancel a connection which is + # not currently being created + else: + if isinstance(peer_address, str): + try: + peer_address = Address(peer_address) + except ValueError: + # If the address is not parsable, assume it is a name instead + logger.debug('looking for peer by name') + peer_address = await self.find_peer_by_name( + peer_address, BT_BR_EDR_TRANSPORT + ) # TODO: timeout + + await self.send_command( + HCI_Create_Connection_Cancel_Command(bd_addr=peer_address), + check_result=True, + ) async def disconnect(self, connection, reason): # Create a future so that we can wait for the disconnection's result @@ -854,43 +2036,108 @@ class Device(CompositeEventEmitter): connection.on('disconnection_failure', pending_disconnection.set_exception) # Request a disconnection - result = await self.send_command(HCI_Disconnect_Command(connection_handle = connection.handle, reason = reason)) + result = await self.send_command( + HCI_Disconnect_Command(connection_handle=connection.handle, reason=reason) + ) try: if result.status != HCI_Command_Status_Event.PENDING: - raise RuntimeError(f'HCI_Disconnect_Command failed: {HCI_Constant.status_name(result.status)} ({result.status})') + raise HCI_StatusError(result) # Wait for the disconnection process to complete self.disconnecting = True - return await pending_disconnection + return await self.abort_on('flush', pending_disconnection) finally: - connection.remove_listener('disconnection', pending_disconnection.set_result) - connection.remove_listener('disconnection_failure', pending_disconnection.set_exception) + connection.remove_listener( + 'disconnection', pending_disconnection.set_result + ) + connection.remove_listener( + 'disconnection_failure', pending_disconnection.set_exception + ) self.disconnecting = False async def update_connection_parameters( self, connection, - conn_interval_min, - conn_interval_max, - conn_latency, + connection_interval_min, + connection_interval_max, + max_latency, supervision_timeout, - minimum_ce_length = 0, - maximum_ce_length = 0 + min_ce_length=0, + max_ce_length=0, ): ''' - NOTE: the name of the parameters may look odd, but it just follows the names used in the Bluetooth spec. + NOTE: the name of the parameters may look odd, but it just follows the names + used in the Bluetooth spec. ''' - await self.send_command(HCI_LE_Connection_Update_Command( - connection_handle = connection.handle, - conn_interval_min = conn_interval_min, - conn_interval_max = conn_interval_max, - conn_latency = conn_latency, - supervision_timeout = supervision_timeout, - minimum_ce_length = minimum_ce_length, - maximum_ce_length = maximum_ce_length - )) - # TODO: check result + result = await self.send_command( + HCI_LE_Connection_Update_Command( + connection_handle=connection.handle, + connection_interval_min=connection_interval_min, + connection_interval_max=connection_interval_max, + max_latency=max_latency, + supervision_timeout=supervision_timeout, + min_ce_length=min_ce_length, + max_ce_length=max_ce_length, + ) + ) + if result.status != HCI_Command_Status_Event.PENDING: + raise HCI_StatusError(result) + + async def get_connection_rssi(self, connection): + result = await self.send_command( + HCI_Read_RSSI_Command(handle=connection.handle), check_result=True + ) + return result.return_parameters.rssi + + async def get_connection_phy(self, connection): + result = await self.send_command( + HCI_LE_Read_PHY_Command(connection_handle=connection.handle), + check_result=True, + ) + return (result.return_parameters.tx_phy, result.return_parameters.rx_phy) + + async def set_connection_phy( + self, connection, tx_phys=None, rx_phys=None, phy_options=None + ): + if not self.host.supports_command(HCI_LE_SET_PHY_COMMAND): + logger.warning('ignoring request, command not supported') + return + + all_phys_bits = (1 if tx_phys is None else 0) | ( + (1 if rx_phys is None else 0) << 1 + ) + + result = await self.send_command( + HCI_LE_Set_PHY_Command( + connection_handle=connection.handle, + all_phys=all_phys_bits, + tx_phys=phy_list_to_bits(tx_phys), + rx_phys=phy_list_to_bits(rx_phys), + phy_options=0 if phy_options is None else int(phy_options), + ) + ) + + if result.status != HCI_COMMAND_STATUS_PENDING: + logger.warning( + 'HCI_LE_Set_PHY_Command failed: ' + f'{HCI_Constant.error_name(result.status)}' + ) + raise HCI_StatusError(result) + + async def set_default_phy(self, tx_phys=None, rx_phys=None): + all_phys_bits = (1 if tx_phys is None else 0) | ( + (1 if rx_phys is None else 0) << 1 + ) + + return await self.send_command( + HCI_LE_Set_Default_PHY_Command( + all_phys=all_phys_bits, + tx_phys=phy_list_to_bits(tx_phys), + rx_phys=phy_list_to_bits(rx_phys), + ), + check_result=True, + ) async def find_peer_by_name(self, name, transport=BT_LE_TRANSPORT): """ @@ -902,23 +2149,26 @@ class Device(CompositeEventEmitter): # Scan/inquire with event handlers to handle scan/inquiry results def on_peer_found(address, ad_data): - local_name = ad_data.get(AdvertisingData.COMPLETE_LOCAL_NAME) + local_name = ad_data.get(AdvertisingData.COMPLETE_LOCAL_NAME, raw=True) if local_name is None: - local_name = ad_data.get(AdvertisingData.SHORTENED_LOCAL_NAME) + local_name = ad_data.get(AdvertisingData.SHORTENED_LOCAL_NAME, raw=True) if local_name is not None: if local_name.decode('utf-8') == name: peer_address.set_result(address) + + handler = None + was_scanning = self.scanning + was_discovering = self.discovering try: - handler = None if transport == BT_LE_TRANSPORT: event_name = 'advertisement' handler = self.on( event_name, - lambda address, ad_data, rssi, connectable: - on_peer_found(address, ad_data) + lambda advertisement: on_peer_found( + advertisement.address, advertisement.data + ), ) - was_scanning = self.scanning if not self.scanning: await self.start_scanning(filter_duplicates=True) @@ -926,17 +2176,17 @@ class Device(CompositeEventEmitter): event_name = 'inquiry_result' handler = self.on( event_name, - lambda address, class_of_device, eir_data, rssi: - on_peer_found(address, eir_data) + lambda address, class_of_device, eir_data, rssi: on_peer_found( + address, eir_data + ), ) - was_discovering = self.discovering if not self.discovering: await self.start_discovery() else: return None - return await peer_address + return await self.abort_on('flush', peer_address) finally: if handler is not None: self.remove_listener(event_name, handler) @@ -947,13 +2197,23 @@ class Device(CompositeEventEmitter): await self.stop_discovery() @property - def pairing_config_factory(self): + def pairing_config_factory(self) -> Callable[[Connection], PairingConfig]: return self.smp_manager.pairing_config_factory @pairing_config_factory.setter - def pairing_config_factory(self, pairing_config_factory): + def pairing_config_factory( + self, pairing_config_factory: Callable[[Connection], PairingConfig] + ) -> None: self.smp_manager.pairing_config_factory = pairing_config_factory + @property + def smp_session_proxy(self) -> Type[smp.Session]: + return self.smp_manager.session_proxy + + @smp_session_proxy.setter + def smp_session_proxy(self, session_proxy: Type[smp.Session]) -> None: + self.smp_manager.session_proxy = session_proxy + async def pair(self, connection): return await self.smp_manager.pair(connection) @@ -976,18 +2236,25 @@ class Device(CompositeEventEmitter): logger.debug('found keys in the key store') if keys.ltk: return keys.ltk.value - elif connection.role == BT_CENTRAL_ROLE and keys.ltk_central: + + if connection.role == BT_CENTRAL_ROLE and keys.ltk_central: return keys.ltk_central.value - elif connection.role == BT_PERIPHERAL_ROLE and keys.ltk_peripheral: + + if connection.role == BT_PERIPHERAL_ROLE and keys.ltk_peripheral: return keys.ltk_peripheral.value - async def get_link_key(self, address): + async def get_link_key(self, address: Address) -> Optional[bytes]: # Look for the key in the keystore if self.keystore is not None: keys = await self.keystore.get(str(address)) if keys is not None: logger.debug('found keys in the key store') + if keys.link_key is None: + logger.warning('no link key') + return None + return keys.link_key.value + return None # [Classic only] async def authenticate(self, connection): @@ -1001,24 +2268,34 @@ class Device(CompositeEventEmitter): pending_authentication.set_exception(HCI_Error(error_code)) connection.on('connection_authentication', on_authentication) - connection.on('connection_authentication_failure', on_authentication_failure) + connection.on('connection_authentication_failure', on_authentication_failure) # Request the authentication try: result = await self.send_command( - HCI_Authentication_Requested_Command(connection_handle = connection.handle) + HCI_Authentication_Requested_Command( + connection_handle=connection.handle + ) ) if result.status != HCI_COMMAND_STATUS_PENDING: - logger.warn(f'HCI_Authentication_Requested_Command failed: {HCI_Constant.error_name(result.status)}') - raise HCI_Error(result.status) + logger.warning( + 'HCI_Authentication_Requested_Command failed: ' + f'{HCI_Constant.error_name(result.status)}' + ) + raise HCI_StatusError(result) # Wait for the authentication to complete - await pending_authentication + await connection.abort_on('disconnection', pending_authentication) finally: connection.remove_listener('connection_authentication', on_authentication) - connection.remove_listener('connection_authentication_failure', on_authentication_failure) + connection.remove_listener( + 'connection_authentication_failure', on_authentication_failure + ) + + async def encrypt(self, connection, enable=True): + if not enable and connection.transport == BT_LE_TRANSPORT: + raise ValueError('`enable` parameter is classic only.') - async def encrypt(self, connection): # Set up event handlers pending_encryption = asyncio.get_running_loop().create_future() @@ -1028,7 +2305,7 @@ class Device(CompositeEventEmitter): def on_encryption_failure(error_code): pending_encryption.set_exception(HCI_Error(error_code)) - connection.on('connection_encryption_change', on_encryption_change) + connection.on('connection_encryption_change', on_encryption_change) connection.on('connection_encryption_failure', on_encryption_failure) # Request the encryption @@ -1043,11 +2320,11 @@ class Device(CompositeEventEmitter): raise RuntimeError('keys not found in key store') if keys.ltk is not None: - ltk = keys.ltk.value + ltk = keys.ltk.value rand = bytes(8) ediv = 0 elif keys.ltk_central is not None: - ltk = keys.ltk_central.value + ltk = keys.ltk_central.value rand = keys.ltk_central.rand ediv = keys.ltk_central.ediv else: @@ -1057,84 +2334,151 @@ class Device(CompositeEventEmitter): raise InvalidStateError('only centrals can start encryption') result = await self.send_command( - HCI_LE_Start_Encryption_Command( - connection_handle = connection.handle, - random_number = rand, - encrypted_diversifier = ediv, - long_term_key = ltk + HCI_LE_Enable_Encryption_Command( + connection_handle=connection.handle, + random_number=rand, + encrypted_diversifier=ediv, + long_term_key=ltk, ) ) if result.status != HCI_COMMAND_STATUS_PENDING: - logger.warn(f'HCI_LE_Start_Encryption_Command failed: {HCI_Constant.error_name(result.status)}') - raise HCI_Error(result.status) + logger.warning( + 'HCI_LE_Enable_Encryption_Command failed: ' + f'{HCI_Constant.error_name(result.status)}' + ) + raise HCI_StatusError(result) else: result = await self.send_command( HCI_Set_Connection_Encryption_Command( - connection_handle = connection.handle, - encryption_enable = 0x01 + connection_handle=connection.handle, + encryption_enable=0x01 if enable else 0x00, ) ) if result.status != HCI_COMMAND_STATUS_PENDING: - logger.warn(f'HCI_Set_Connection_Encryption_Command failed: {HCI_Constant.error_name(result.status)}') - raise HCI_Error(result.status) + logger.warning( + 'HCI_Set_Connection_Encryption_Command failed: ' + f'{HCI_Constant.error_name(result.status)}' + ) + raise HCI_StatusError(result) # Wait for the result - await pending_encryption + await connection.abort_on('disconnection', pending_encryption) + finally: + connection.remove_listener( + 'connection_encryption_change', on_encryption_change + ) + connection.remove_listener( + 'connection_encryption_failure', on_encryption_failure + ) + + # [Classic only] + async def switch_role(self, connection: Connection, role: int): + pending_role_change = asyncio.get_running_loop().create_future() + + def on_role_change(new_role): + pending_role_change.set_result(new_role) + + def on_role_change_failure(error_code): + pending_role_change.set_exception(HCI_Error(error_code)) + + connection.on('role_change', on_role_change) + connection.on('role_change_failure', on_role_change_failure) + + try: + result = await self.send_command( + HCI_Switch_Role_Command(bd_addr=connection.peer_address, role=role) # type: ignore[call-arg] + ) + if result.status != HCI_COMMAND_STATUS_PENDING: + logger.warning( + 'HCI_Switch_Role_Command failed: ' + f'{HCI_Constant.error_name(result.status)}' + ) + raise HCI_StatusError(result) + await connection.abort_on('disconnection', pending_role_change) finally: - connection.remove_listener('connection_encryption_change', on_encryption_change) - connection.remove_listener('connection_encryption_failure', on_encryption_failure) + connection.remove_listener('role_change', on_role_change) + connection.remove_listener('role_change_failure', on_role_change_failure) # [Classic only] - async def request_remote_name(self, connection): + async def request_remote_name(self, remote: Union[Address, Connection]) -> str: # Set up event handlers pending_name = asyncio.get_running_loop().create_future() - def on_remote_name(): - pending_name.set_result(connection.peer_name) - - def on_remote_name_failure(error_code): - pending_name.set_exception(HCI_Error(error_code)) + peer_address = remote if isinstance(remote, Address) else remote.peer_address - connection.on('remote_name', on_remote_name) - connection.on('remote_name_failure', on_remote_name_failure) + handler = self.on( + 'remote_name', + lambda address, remote_name: pending_name.set_result(remote_name) + if address == peer_address + else None, + ) + failure_handler = self.on( + 'remote_name_failure', + lambda address, error_code: pending_name.set_exception( + HCI_Error(error_code) + ) + if address == peer_address + else None, + ) try: result = await self.send_command( HCI_Remote_Name_Request_Command( - bd_addr = connection.peer_address, - page_scan_repetition_mode = HCI_Remote_Name_Request_Command.R0, # TODO investigate other options - reserved = 0, - clock_offset = 0 # TODO investigate non-0 values - ) + bd_addr=peer_address, + page_scan_repetition_mode=HCI_Remote_Name_Request_Command.R2, + reserved=0, + clock_offset=0, # TODO investigate non-0 values + ) # type: ignore[call-arg] ) if result.status != HCI_COMMAND_STATUS_PENDING: - logger.warn(f'HCI_Set_Connection_Encryption_Command failed: {HCI_Constant.error_name(result.status)}') - raise HCI_Error(result.status) + logger.warning( + 'HCI_Set_Connection_Encryption_Command failed: ' + f'{HCI_Constant.error_name(result.status)}' + ) + raise HCI_StatusError(result) # Wait for the result - return await pending_name + return await self.abort_on('flush', pending_name) finally: - connection.remove_listener('remote_name', on_remote_name) - connection.remove_listener('remote_name_failure', on_remote_name_failure) + self.remove_listener('remote_name', handler) + self.remove_listener('remote_name_failure', failure_handler) + + @host_event_handler + def on_flush(self): + self.emit('flush') + for _, connection in self.connections.items(): + connection.emit('disconnection', 0) + self.connections = {} # [Classic only] @host_event_handler def on_link_key(self, bd_addr, link_key, key_type): # Store the keys in the key store if self.keystore: - pairing_keys = keys.PairingKeys() - pairing_keys.link_key = keys.PairingKeys.Key(value = link_key) + authenticated = key_type in ( + HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE, + HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE, + ) + pairing_keys = PairingKeys() + pairing_keys.link_key = PairingKeys.Key( + value=link_key, authenticated=authenticated + ) async def store_keys(): try: await self.keystore.update(str(bd_addr), pairing_keys) except Exception as error: - logger.warn(f'!!! error while storing keys: {error}') + logger.warning(f'!!! error while storing keys: {error}') + + self.abort_on('flush', store_keys()) - asyncio.create_task(store_keys()) + if connection := self.find_connection_by_bd_addr( + bd_addr, transport=BT_BR_EDR_TRANSPORT + ): + connection.link_key_type = key_type def add_service(self, service): self.gatt_server.add_service(service) @@ -1142,65 +2486,184 @@ class Device(CompositeEventEmitter): def add_services(self, services): self.gatt_server.add_services(services) - async def notify_subscriber(self, connection, attribute, force=False): - await self.gatt_server.notify_subscriber(connection, attribute, force) + def add_default_services(self, generic_access_service=True): + # Add a GAP Service if requested + if generic_access_service: + self.gatt_server.add_service(GenericAccessService(self.name)) + + async def notify_subscriber(self, connection, attribute, value=None, force=False): + await self.gatt_server.notify_subscriber(connection, attribute, value, force) - async def notify_subscribers(self, attribute, force=False): - await self.gatt_server.notify_subscribers(attribute, force) + async def notify_subscribers(self, attribute, value=None, force=False): + await self.gatt_server.notify_subscribers(attribute, value, force) - async def indicate_subscriber(self, connection, attribute, force=False): - await self.gatt_server.indicate_subscriber(connection, attribute, force) + async def indicate_subscriber(self, connection, attribute, value=None, force=False): + await self.gatt_server.indicate_subscriber(connection, attribute, value, force) - async def indicate_subscribers(self, attribute): - await self.gatt_server.indicate_subscribers(attribute) + async def indicate_subscribers(self, attribute, value=None, force=False): + await self.gatt_server.indicate_subscribers(attribute, value, force) @host_event_handler - def on_connection(self, connection_handle, transport, peer_address, peer_resolvable_address, role, connection_parameters): - logger.debug(f'*** Connection: [0x{connection_handle:04X}] {peer_address} as {HCI_Constant.role_name(role)}') - if connection_handle in self.connections: - logger.warn('new connection reuses the same handle as a previous connection') - - # Resolve the peer address if we can - if self.address_resolver: - if peer_address.is_resolvable: - resolved_address = self.address_resolver.resolve(peer_address) - if resolved_address is not None: - logger.debug(f'*** Address resolved as {resolved_address}') - peer_resolvable_address = peer_address - peer_address = resolved_address - - # Create a new connection - connection = Connection( - self, - connection_handle, - transport, - peer_address, - peer_resolvable_address, - role, - connection_parameters + def on_connection( + self, + connection_handle, + transport, + peer_address, + role, + connection_parameters, + ): + logger.debug( + f'*** Connection: [0x{connection_handle:04X}] ' + f'{peer_address} {"" if role is None else HCI_Constant.role_name(role)}' ) - self.connections[connection_handle] = connection + if connection_handle in self.connections: + logger.warning( + 'new connection reuses the same handle as a previous connection' + ) - # We are no longer advertising - self.advertising = False + peer_resolvable_address = None + + if transport == BT_BR_EDR_TRANSPORT: + # Create a new connection + connection = self.pending_connections.pop(peer_address) + connection.complete(connection_handle, connection_parameters) + self.connections[connection_handle] = connection + + # Emit an event to notify listeners of the new connection + self.emit('connection', connection) + else: + # Resolve the peer address if we can + if self.address_resolver: + if peer_address.is_resolvable: + resolved_address = self.address_resolver.resolve(peer_address) + if resolved_address is not None: + logger.debug(f'*** Address resolved as {resolved_address}') + peer_resolvable_address = peer_address + peer_address = resolved_address + + # Guess which own address type is used for this connection. + # This logic is somewhat correct but may need to be improved + # when multiple advertising are run simultaneously. + if self.connect_own_address_type is not None: + own_address_type = self.connect_own_address_type + else: + own_address_type = self.advertising_own_address_type + + # We are no longer advertising + self.advertising_own_address_type = None + self.advertising = False + + if own_address_type in ( + OwnAddressType.PUBLIC, + OwnAddressType.RESOLVABLE_OR_PUBLIC, + ): + self_address = self.public_address + else: + self_address = self.random_address + + # Create a new connection + connection = Connection( + self, + connection_handle, + transport, + self_address, + peer_address, + peer_resolvable_address, + role, + connection_parameters, + ConnectionPHY(HCI_LE_1M_PHY, HCI_LE_1M_PHY), + ) + self.connections[connection_handle] = connection - # Emit an event to notify listeners of the new connection - self.emit('connection', connection) + # If supported, read which PHY we're connected with before + # notifying listeners of the new connection. + if self.host.supports_command(HCI_LE_READ_PHY_COMMAND): + + async def read_phy(): + result = await self.send_command( + HCI_LE_Read_PHY_Command(connection_handle=connection_handle), + check_result=True, + ) + connection.phy = ConnectionPHY( + result.return_parameters.tx_phy, result.return_parameters.rx_phy + ) + # Emit an event to notify listeners of the new connection + self.emit('connection', connection) + + # Do so asynchronously to not block the current event handler + connection.abort_on('disconnection', read_phy()) + + else: + # Emit an event to notify listeners of the new connection + self.emit('connection', connection) @host_event_handler - def on_connection_failure(self, error_code): - logger.debug(f'*** Connection failed: {error_code}') - error = ConnectionError( + def on_connection_failure(self, transport, peer_address, error_code): + logger.debug(f'*** Connection failed: {HCI_Constant.error_name(error_code)}') + + # For directed advertising, this means a timeout + if ( + transport == BT_LE_TRANSPORT + and self.advertising + and self.advertising_type.is_directed + ): + self.advertising_own_address_type = None + self.advertising = False + + # Notify listeners + error = core.ConnectionError( error_code, + transport, + peer_address, 'hci', - HCI_Constant.error_name(error_code) + HCI_Constant.error_name(error_code), ) self.emit('connection_failure', error) + # FIXME: Explore a delegate-model for BR/EDR wait connection #56. + @host_event_handler + def on_connection_request(self, bd_addr, class_of_device, link_type): + logger.debug(f'*** Connection request: {bd_addr}') + + # match a pending future using `bd_addr` + if bd_addr in self.classic_pending_accepts: + future, *_ = self.classic_pending_accepts.pop(bd_addr) + future.set_result((bd_addr, class_of_device, link_type)) + + # match first pending future for ANY address + elif len(self.classic_pending_accepts[Address.ANY]) > 0: + future = self.classic_pending_accepts[Address.ANY].pop(0) + future.set_result((bd_addr, class_of_device, link_type)) + + # device configuration is set to accept any incoming connection + elif self.classic_accept_any: + # Save pending connection + self.pending_connections[bd_addr] = Connection.incomplete( + self, bd_addr, BT_PERIPHERAL_ROLE + ) + + self.host.send_command_sync( + HCI_Accept_Connection_Request_Command( + bd_addr=bd_addr, role=0x01 # Remain the peripheral + ) + ) + + # reject incoming connection + else: + self.host.send_command_sync( + HCI_Reject_Connection_Request_Command( + bd_addr=bd_addr, + reason=HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR, + ) + ) + @host_event_handler @with_connection_from_handle def on_disconnection(self, connection, reason): - logger.debug(f'*** Disconnection: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, reason={reason}') + logger.debug( + f'*** Disconnection: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}, reason={reason}' + ) connection.emit('disconnection', reason) # Remove the connection from the map @@ -1212,39 +2675,70 @@ class Device(CompositeEventEmitter): # Restart advertising if auto-restart is enabled if self.auto_restart_advertising: logger.debug('restarting advertising') - asyncio.create_task(self.start_advertising(auto_restart=self.auto_restart_advertising)) + self.abort_on( + 'flush', + self.start_advertising( + advertising_type=self.advertising_type, auto_restart=True + ), + ) @host_event_handler @with_connection_from_handle def on_disconnection_failure(self, connection, error_code): logger.debug(f'*** Disconnection failed: {error_code}') - error = ConnectionError( + error = core.ConnectionError( error_code, + connection.transport, + connection.peer_address, 'hci', - HCI_Constant.error_name(error_code) + HCI_Constant.error_name(error_code), ) connection.emit('disconnection_failure', error) @host_event_handler @AsyncRunner.run_in_task() async def on_inquiry_complete(self): - if self.discovering: + if self.auto_restart_inquiry: # Inquire again - await self.start_discovery() + await self.start_discovery(auto_restart=True) + else: + self.auto_restart_inquiry = True + self.discovering = False + self.emit('inquiry_complete') @host_event_handler @with_connection_from_handle def on_connection_authentication(self, connection): - logger.debug(f'*** Connection Authentication: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}') + logger.debug( + f'*** Connection Authentication: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}' + ) connection.authenticated = True connection.emit('connection_authentication') @host_event_handler @with_connection_from_handle def on_connection_authentication_failure(self, connection, error): - logger.debug(f'*** Connection Authentication Failure: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, error={error}') + logger.debug( + f'*** Connection Authentication Failure: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}, error={error}' + ) connection.emit('connection_authentication_failure', error) + @host_event_handler + @with_connection_from_address + def on_ssp_complete(self, connection): + # On Secure Simple Pairing complete, in case: + # - Connection isn't already authenticated + # - AND we are not the initiator of the authentication + # We must trigger authentication to know if we are truly authenticated + if not connection.authenticating and not connection.authenticated: + logger.debug( + f'*** Trigger Connection Authentication: [0x{connection.handle:04X}] ' + f'{connection.peer_address}' + ) + asyncio.create_task(connection.authenticate()) + # [Classic only] @host_event_handler @with_connection_from_address @@ -1252,197 +2746,356 @@ class Device(CompositeEventEmitter): # Ask what the pairing config should be for this connection pairing_config = self.pairing_config_factory(connection) - # Map the SMP IO capability to a Classic IO capability - io_capability = { - smp.SMP_DISPLAY_ONLY_IO_CAPABILITY: HCI_DISPLAY_ONLY_IO_CAPABILITY, - smp.SMP_DISPLAY_YES_NO_IO_CAPABILITY: HCI_DISPLAY_YES_NO_IO_CAPABILITY, - smp.SMP_KEYBOARD_ONLY_IO_CAPABILITY: HCI_KEYBOARD_ONLY_IO_CAPABILITY, - smp.SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY, - smp.SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: HCI_DISPLAY_YES_NO_IO_CAPABILITY - }.get(pairing_config.delegate.io_capability) - - if io_capability is None: - logger.warning(f'cannot map IO capability ({pairing_config.delegate.io_capability}') - io_capability = HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY - # Compute the authentication requirements authentication_requirements = ( # No Bonding ( HCI_MITM_NOT_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS, - HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS + HCI_MITM_REQUIRED_NO_BONDING_AUTHENTICATION_REQUIREMENTS, ), # General Bonding ( HCI_MITM_NOT_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS, - HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS - ) + HCI_MITM_REQUIRED_GENERAL_BONDING_AUTHENTICATION_REQUIREMENTS, + ), )[1 if pairing_config.bonding else 0][1 if pairing_config.mitm else 0] # Respond self.host.send_command_sync( HCI_IO_Capability_Request_Reply_Command( - bd_addr = connection.peer_address, - io_capability = io_capability, - oob_data_present = 0x00, # Not present - authentication_requirements = authentication_requirements + bd_addr=connection.peer_address, + io_capability=pairing_config.delegate.classic_io_capability, + oob_data_present=0x00, # Not present + authentication_requirements=authentication_requirements, ) ) # [Classic only] @host_event_handler @with_connection_from_address - def on_authentication_user_confirmation_request(self, connection, code): + def on_authentication_io_capability_response( + self, connection, io_capability, authentication_requirements + ): + connection.peer_pairing_io_capability = io_capability + connection.peer_pairing_authentication_requirements = ( + authentication_requirements + ) + + # [Classic only] + @host_event_handler + @with_connection_from_address + def on_authentication_user_confirmation_request(self, connection, code) -> None: # Ask what the pairing config should be for this connection pairing_config = self.pairing_config_factory(connection) - - can_confirm = pairing_config.delegate.io_capability not in { - smp.SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY, - smp.SMP_DISPLAY_ONLY_IO_CAPABILITY + io_capability = pairing_config.delegate.classic_io_capability + peer_io_capability = connection.peer_pairing_io_capability + + async def confirm() -> bool: + # Ask the user to confirm the pairing, without display + return await pairing_config.delegate.confirm() + + async def auto_confirm() -> bool: + # Ask the user to auto-confirm the pairing, without display + return await pairing_config.delegate.confirm(auto=True) + + async def display_confirm() -> bool: + # Display the code and ask the user to compare + return await pairing_config.delegate.compare_numbers(code, digits=6) + + async def display_auto_confirm() -> bool: + # Display the code to the user and ask the delegate to auto-confirm + await pairing_config.delegate.display_number(code, digits=6) + return await pairing_config.delegate.confirm(auto=True) + + async def na() -> bool: + assert False, "N/A: unreachable" + + # See Bluetooth spec @ Vol 3, Part C 5.2.2.6 + methods = { + HCI_DISPLAY_ONLY_IO_CAPABILITY: { + HCI_DISPLAY_ONLY_IO_CAPABILITY: display_auto_confirm, + HCI_DISPLAY_YES_NO_IO_CAPABILITY: display_confirm, + HCI_KEYBOARD_ONLY_IO_CAPABILITY: na, + HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm, + }, + HCI_DISPLAY_YES_NO_IO_CAPABILITY: { + HCI_DISPLAY_ONLY_IO_CAPABILITY: display_auto_confirm, + HCI_DISPLAY_YES_NO_IO_CAPABILITY: display_confirm, + HCI_KEYBOARD_ONLY_IO_CAPABILITY: na, + HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm, + }, + HCI_KEYBOARD_ONLY_IO_CAPABILITY: { + HCI_DISPLAY_ONLY_IO_CAPABILITY: na, + HCI_DISPLAY_YES_NO_IO_CAPABILITY: na, + HCI_KEYBOARD_ONLY_IO_CAPABILITY: na, + HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm, + }, + HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: { + HCI_DISPLAY_ONLY_IO_CAPABILITY: confirm, + HCI_DISPLAY_YES_NO_IO_CAPABILITY: confirm, + HCI_KEYBOARD_ONLY_IO_CAPABILITY: auto_confirm, + HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: auto_confirm, + }, } - # Respond - if can_confirm and pairing_config.delegate: - async def compare_numbers(): - numbers_match = await pairing_config.delegate.compare_numbers(code, digits=6) - if numbers_match: - self.host.send_command_sync( - HCI_User_Confirmation_Request_Reply_Command(bd_addr=connection.peer_address) + method = methods[peer_io_capability][io_capability] + + async def reply() -> None: + if await connection.abort_on('disconnection', method()): + await self.host.send_command( + HCI_User_Confirmation_Request_Reply_Command( # type: ignore[call-arg] + bd_addr=connection.peer_address ) - else: - self.host.send_command_sync( - HCI_User_Confirmation_Request_Negative_Reply_Command(bd_addr=connection.peer_address) + ) + else: + await self.host.send_command( + HCI_User_Confirmation_Request_Negative_Reply_Command( # type: ignore[call-arg] + bd_addr=connection.peer_address ) + ) - asyncio.create_task(compare_numbers()) - else: - self.host.send_command_sync( - HCI_User_Confirmation_Request_Reply_Command(bd_addr=connection.peer_address) - ) + AsyncRunner.spawn(reply()) # [Classic only] @host_event_handler @with_connection_from_address - def on_authentication_user_passkey_request(self, connection): + def on_authentication_user_passkey_request(self, connection) -> None: # Ask what the pairing config should be for this connection pairing_config = self.pairing_config_factory(connection) - can_input = pairing_config.delegate.io_capability in { - smp.SMP_KEYBOARD_ONLY_IO_CAPABILITY, - smp.SMP_KEYBOARD_DISPLAY_IO_CAPABILITY - } + async def reply() -> None: + number = await connection.abort_on( + 'disconnection', pairing_config.delegate.get_number() + ) + if number is not None: + await self.host.send_command( + HCI_User_Passkey_Request_Reply_Command( # type: ignore[call-arg] + bd_addr=connection.peer_address, numeric_value=number + ) + ) + else: + await self.host.send_command( + HCI_User_Passkey_Request_Negative_Reply_Command( # type: ignore[call-arg] + bd_addr=connection.peer_address + ) + ) + + AsyncRunner.spawn(reply()) + + # [Classic only] + @host_event_handler + @with_connection_from_address + def on_pin_code_request(self, connection): + # Classic legacy pairing + # Ask what the pairing config should be for this connection + pairing_config = self.pairing_config_factory(connection) + io_capability = pairing_config.delegate.classic_io_capability # Respond - if can_input and pairing_config.delegate: - async def get_number(): - number = await pairing_config.delegate.get_number() - if number is not None: - self.host.send_command_sync( - HCI_User_Passkey_Request_Reply_Command( - bd_addr = connection.peer_address, - numeric_value = number) + if io_capability == HCI_KEYBOARD_ONLY_IO_CAPABILITY: + # Ask the user to enter a string + async def get_pin_code(): + pin_code = await connection.abort_on( + 'disconnection', pairing_config.delegate.get_string(16) + ) + + if pin_code is not None: + pin_code = bytes(pin_code, encoding='utf-8') + pin_code_len = len(pin_code) + assert 0 < pin_code_len <= 16, "pin_code should be 1-16 bytes" + await self.host.send_command( + HCI_PIN_Code_Request_Reply_Command( + bd_addr=connection.peer_address, + pin_code_length=pin_code_len, + pin_code=pin_code, + ) ) else: - self.host.send_command_sync( - HCI_User_Passkey_Request_Negative_Reply_Command(bd_addr=connection.peer_address) + logger.debug("delegate.get_string() returned None") + await self.host.send_command( + HCI_PIN_Code_Request_Negative_Reply_Command( + bd_addr=connection.peer_address + ) ) - asyncio.create_task(get_number()) + asyncio.create_task(get_pin_code()) else: self.host.send_command_sync( - HCI_User_Passkey_Request_Negative_Reply_Command(bd_addr=connection.peer_address) + HCI_PIN_Code_Request_Negative_Reply_Command( + bd_addr=connection.peer_address + ) ) # [Classic only] @host_event_handler @with_connection_from_address - def on_remote_name(self, connection, remote_name): + def on_authentication_user_passkey_notification(self, connection, passkey): + # Ask what the pairing config should be for this connection + pairing_config = self.pairing_config_factory(connection) + + # Show the passkey to the user + connection.abort_on( + 'disconnection', pairing_config.delegate.display_number(passkey) + ) + + # [Classic only] + @host_event_handler + @try_with_connection_from_address + def on_remote_name(self, connection: Connection, address, remote_name): # Try to decode the name try: - connection.peer_name = remote_name.decode('utf-8') - connection.emit('remote_name') + remote_name = remote_name.decode('utf-8') + if connection: + connection.peer_name = remote_name + connection.emit('remote_name') + self.emit('remote_name', address, remote_name) except UnicodeDecodeError as error: logger.warning('peer name is not valid UTF-8') - connection.emit('remote_name_failure', error) + if connection: + connection.emit('remote_name_failure', error) + else: + self.emit('remote_name_failure', address, error) # [Classic only] @host_event_handler - @with_connection_from_address - def on_remote_name_failure(self, connection, error): - connection.emit('remote_name_failure', error) + @try_with_connection_from_address + def on_remote_name_failure(self, connection: Connection, address, error): + if connection: + connection.emit('remote_name_failure', error) + self.emit('remote_name_failure', address, error) @host_event_handler @with_connection_from_handle def on_connection_encryption_change(self, connection, encryption): - logger.debug(f'*** Connection Encryption Change: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, encryption={encryption}') + logger.debug( + f'*** Connection Encryption Change: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}, ' + f'encryption={encryption}' + ) connection.encryption = encryption + if ( + not connection.authenticated + and encryption == HCI_Encryption_Change_Event.AES_CCM + ): + connection.authenticated = True + connection.sc = True connection.emit('connection_encryption_change') @host_event_handler @with_connection_from_handle def on_connection_encryption_failure(self, connection, error): - logger.debug(f'*** Connection Encryption Failure: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, error={error}') + logger.debug( + f'*** Connection Encryption Failure: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}, ' + f'error={error}' + ) connection.emit('connection_encryption_failure', error) @host_event_handler @with_connection_from_handle def on_connection_encryption_key_refresh(self, connection): - logger.debug(f'*** Connection Key Refresh: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}') + logger.debug( + f'*** Connection Key Refresh: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}' + ) connection.emit('connection_encryption_key_refresh') @host_event_handler @with_connection_from_handle def on_connection_parameters_update(self, connection, connection_parameters): - logger.debug(f'*** Connection Parameters Update: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, {connection_parameters}') + logger.debug( + f'*** Connection Parameters Update: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}, ' + f'{connection_parameters}' + ) connection.parameters = connection_parameters connection.emit('connection_parameters_update') @host_event_handler @with_connection_from_handle def on_connection_parameters_update_failure(self, connection, error): - logger.debug(f'*** Connection Parameters Update Failed: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, error={error}') + logger.debug( + f'*** Connection Parameters Update Failed: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}, ' + f'error={error}' + ) connection.emit('connection_parameters_update_failure', error) @host_event_handler @with_connection_from_handle def on_connection_phy_update(self, connection, connection_phy): - logger.debug(f'*** Connection PHY Update: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, {connection_phy}') + logger.debug( + f'*** Connection PHY Update: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}, ' + f'{connection_phy}' + ) connection.phy = connection_phy connection.emit('connection_phy_update') @host_event_handler @with_connection_from_handle def on_connection_phy_update_failure(self, connection, error): - logger.debug(f'*** Connection PHY Update Failed: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, error={error}') + logger.debug( + f'*** Connection PHY Update Failed: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}, ' + f'error={error}' + ) connection.emit('connection_phy_update_failure', error) @host_event_handler @with_connection_from_handle def on_connection_att_mtu_update(self, connection, att_mtu): - logger.debug(f'*** Connection ATT MTU Update: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}, {att_mtu}') + logger.debug( + f'*** Connection ATT MTU Update: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}, ' + f'{att_mtu}' + ) connection.att_mtu = att_mtu connection.emit('connection_att_mtu_update') @host_event_handler @with_connection_from_handle - def on_connection_data_length_change(self, connection, max_tx_octets, max_tx_time, max_rx_octets, max_rx_time): - logger.debug(f'*** Connection Data Length Change: [0x{connection.handle:04X}] {connection.peer_address} as {connection.role_name}') - connection.data_length = (max_tx_octets, max_tx_time, max_rx_octets, max_rx_time) + def on_connection_data_length_change( + self, connection, max_tx_octets, max_tx_time, max_rx_octets, max_rx_time + ): + logger.debug( + f'*** Connection Data Length Change: [0x{connection.handle:04X}] ' + f'{connection.peer_address} as {connection.role_name}' + ) + connection.data_length = ( + max_tx_octets, + max_tx_time, + max_rx_octets, + max_rx_time, + ) connection.emit('connection_data_length_change') - @with_connection_from_handle - def on_pairing_start(self, connection): + # [Classic only] + @host_event_handler + @with_connection_from_address + def on_role_change(self, connection, new_role): + connection.role = new_role + connection.emit('role_change', new_role) + + # [Classic only] + @host_event_handler + @try_with_connection_from_address + def on_role_change_failure(self, connection, address, error): + if connection: + connection.emit('role_change_failure', error) + self.emit('role_change_failure', address, error) + + def on_pairing_start(self, connection: Connection) -> None: connection.emit('pairing_start') - @with_connection_from_handle - def on_pairing(self, connection, keys): + def on_pairing(self, connection: Connection, keys: PairingKeys, sc: bool) -> None: + connection.sc = sc + connection.authenticated = True connection.emit('pairing', keys) - @with_connection_from_handle - def on_pairing_failure(self, connection, reason): + def on_pairing_failure(self, connection: Connection, reason: int) -> None: connection.emit('pairing_failure', reason) - @host_event_handler @with_connection_from_handle def on_gatt_pdu(self, connection, pdu): # Parse the L2CAP payload into an ATT PDU object @@ -1452,16 +3105,19 @@ class Device(CompositeEventEmitter): # odd-numbered ones are server->client if att_pdu.op_code & 1: if connection.gatt_client is None: - logger.warn(color('no GATT client for connection 0x{connection_handle:04X}')) + logger.warning( + color('no GATT client for connection 0x{connection_handle:04X}') + ) return connection.gatt_client.on_gatt_pdu(att_pdu) else: if connection.gatt_server is None: - logger.warn(color('no GATT server for connection 0x{connection_handle:04X}')) + logger.warning( + color('no GATT server for connection 0x{connection_handle:04X}') + ) return connection.gatt_server.on_gatt_pdu(connection, att_pdu) - @host_event_handler @with_connection_from_handle def on_smp_pdu(self, connection, pdu): self.smp_manager.on_smp_pdu(connection, pdu) @@ -1472,4 +3128,8 @@ class Device(CompositeEventEmitter): self.l2cap_channel_manager.on_pdu(connection, cid, pdu) def __str__(self): - return f'Device(name="{self.name}", random_address="{self.random_address}"", public_address="{self.public_address}")' + return ( + f'Device(name="{self.name}", ' + f'random_address="{self.random_address}", ' + f'public_address="{self.public_address}")' + ) diff --git a/bumble/gap.py b/bumble/gap.py index 8341215..29df89f 100644 --- a/bumble/gap.py +++ b/bumble/gap.py @@ -23,7 +23,7 @@ from .gatt import ( Characteristic, GATT_GENERIC_ACCESS_SERVICE, GATT_DEVICE_NAME_CHARACTERISTIC, - GATT_APPEARANCE_CHARACTERISTIC + GATT_APPEARANCE_CHARACTERISTIC, ) # ----------------------------------------------------------------------------- @@ -38,22 +38,22 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- class GenericAccessService(Service): - def __init__(self, device_name, appearance = (0, 0)): + def __init__(self, device_name, appearance=(0, 0)): device_name_characteristic = Characteristic( GATT_DEVICE_NAME_CHARACTERISTIC, - Characteristic.READ, + Characteristic.Properties.READ, Characteristic.READABLE, - device_name.encode('utf-8')[:248] + device_name.encode('utf-8')[:248], ) appearance_characteristic = Characteristic( GATT_APPEARANCE_CHARACTERISTIC, - Characteristic.READ, + Characteristic.Properties.READ, Characteristic.READABLE, - struct.pack('<H', (appearance[0] << 6) | appearance[1]) + struct.pack('<H', (appearance[0] << 6) | appearance[1]), ) - super().__init__(GATT_GENERIC_ACCESS_SERVICE, [ - device_name_characteristic, - appearance_characteristic - ]) + super().__init__( + GATT_GENERIC_ACCESS_SERVICE, + [device_name_characteristic, appearance_characteristic], + ) diff --git a/bumble/gatt.py b/bumble/gatt.py index df760c3..ea2b690 100644 --- a/bumble/gatt.py +++ b/bumble/gatt.py @@ -22,14 +22,18 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import asyncio -import types +import enum +import functools import logging -from colors import color +import struct +from typing import Optional, Sequence, List + +from .colors import color +from .core import UUID, get_dict_key_by_value +from .att import Attribute -from .core import * -from .hci import * -from .att import * # ----------------------------------------------------------------------------- # Logging @@ -39,6 +43,9 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off +# pylint: disable=line-too-long + GATT_REQUEST_TIMEOUT = 30 # seconds GATT_MAX_ATTRIBUTE_VALUE_SIZE = 512 @@ -134,13 +141,29 @@ GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC = UUID.from_16_bits(0x2A2 GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC = UUID.from_16_bits(0x2A2A, 'IEEE 11073-20601 Regulatory Certification Data List') GATT_PNP_ID_CHARACTERISTIC = UUID.from_16_bits(0x2A50, 'PnP ID') -# Human Interface Device +# Human Interface Device Service GATT_HID_INFORMATION_CHARACTERISTIC = UUID.from_16_bits(0x2A4A, 'HID Information') GATT_REPORT_MAP_CHARACTERISTIC = UUID.from_16_bits(0x2A4B, 'Report Map') GATT_HID_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2A4C, 'HID Control Point') GATT_REPORT_CHARACTERISTIC = UUID.from_16_bits(0x2A4D, 'Report') GATT_PROTOCOL_MODE_CHARACTERISTIC = UUID.from_16_bits(0x2A4E, 'Protocol Mode') +# Heart Rate Service +GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC = UUID.from_16_bits(0x2A37, 'Heart Rate Measurement') +GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC = UUID.from_16_bits(0x2A38, 'Body Sensor Location') +GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC = UUID.from_16_bits(0x2A39, 'Heart Rate Control Point') + +# Battery Service +GATT_BATTERY_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A19, 'Battery Level') + +# ASHA Service +GATT_ASHA_SERVICE = UUID.from_16_bits(0xFDF0, 'Audio Streaming for Hearing Aid') +GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC = UUID('6333651e-c481-4a3e-9169-7c902aad37bb', 'ReadOnlyProperties') +GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC = UUID('f0d4de7e-4a88-476c-9d9f-1937b0996cc0', 'AudioControlPoint') +GATT_ASHA_AUDIO_STATUS_CHARACTERISTIC = UUID('38663f1a-e711-4cac-b641-326b56404837', 'AudioStatus') +GATT_ASHA_VOLUME_CHARACTERISTIC = UUID('00e4ca9e-ab14-41e4-8823-f9e70c7e91df', 'Volume') +GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC = UUID('2d410339-82b6-42aa-b34e-e2e01df8cc1a', 'LE_PSM_OUT') + # Misc GATT_DEVICE_NAME_CHARACTERISTIC = UUID.from_16_bits(0x2A00, 'Device Name') GATT_APPEARANCE_CHARACTERISTIC = UUID.from_16_bits(0x2A01, 'Appearance') @@ -150,17 +173,20 @@ GATT_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS_CHARACTERISTIC = UUID.from_16_bi GATT_SERVICE_CHANGED_CHARACTERISTIC = UUID.from_16_bits(0x2A05, 'Service Changed') GATT_ALERT_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A06, 'Alert Level') GATT_TX_POWER_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A07, 'Tx Power Level') -GATT_BATTERY_LEVEL_CHARACTERISTIC = UUID.from_16_bits(0x2A19, 'Battery Level') GATT_BOOT_KEYBOARD_INPUT_REPORT_CHARACTERISTIC = UUID.from_16_bits(0x2A22, 'Boot Keyboard Input Report') GATT_CURRENT_TIME_CHARACTERISTIC = UUID.from_16_bits(0x2A2B, 'Current Time') GATT_BOOT_KEYBOARD_OUTPUT_REPORT_CHARACTERISTIC = UUID.from_16_bits(0x2A32, 'Boot Keyboard Output Report') GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC = UUID.from_16_bits(0x2AA6, 'Central Address Resolution') +# fmt: on +# pylint: enable=line-too-long + # ----------------------------------------------------------------------------- # Utils # ----------------------------------------------------------------------------- + def show_services(services): for service in services: print(color(str(service), 'cyan')) @@ -178,23 +204,48 @@ class Service(Attribute): See Vol 3, Part G - 3.1 SERVICE DEFINITION ''' - def __init__(self, uuid, characteristics, primary=True): + uuid: UUID + characteristics: List[Characteristic] + included_services: List[Service] + + def __init__( + self, + uuid, + characteristics: List[Characteristic], + primary=True, + included_services: List[Service] = [], + ): # Convert the uuid to a UUID object if it isn't already - if type(uuid) is str: + if isinstance(uuid, str): uuid = UUID(uuid) super().__init__( - GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE if primary else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, + GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE + if primary + else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, Attribute.READABLE, - uuid.to_pdu_bytes() + uuid.to_pdu_bytes(), ) - self.uuid = uuid - self.included_services = [] - self.characteristics = characteristics[:] - self.primary = primary + self.uuid = uuid + self.included_services = included_services[:] + self.characteristics = characteristics[:] + self.primary = primary + + def get_advertising_data(self) -> Optional[bytes]: + """ + Get Service specific advertising data + Defined by each Service, default value is empty + :return Service data for advertising + """ + return None def __str__(self): - return f'Service(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}){"" if self.primary else "*"}' + return ( + f'Service(handle=0x{self.handle:04X}, ' + f'end=0x{self.end_group_handle:04X}, ' + f'uuid={self.uuid})' + f'{"" if self.primary else "*"}' + ) # ----------------------------------------------------------------------------- @@ -203,63 +254,155 @@ class TemplateService(Service): Convenience abstract class that can be used by profile-specific subclasses that want to expose their UUID as a class property ''' - UUID = None + + UUID: Optional[UUID] = None def __init__(self, characteristics, primary=True): super().__init__(self.UUID, characteristics, primary) # ----------------------------------------------------------------------------- +class IncludedServiceDeclaration(Attribute): + ''' + See Vol 3, Part G - 3.2 INCLUDE DEFINITION + ''' + + service: Service + + def __init__(self, service): + declaration_bytes = struct.pack( + '<HH2s', service.handle, service.end_group_handle, service.uuid.to_bytes() + ) + super().__init__( + GATT_INCLUDE_ATTRIBUTE_TYPE, Attribute.READABLE, declaration_bytes + ) + self.service = service + + def __str__(self): + return ( + f'IncludedServiceDefinition(handle=0x{self.handle:04X}, ' + f'group_starting_handle=0x{self.service.handle:04X}, ' + f'group_ending_handle=0x{self.service.end_group_handle:04X}, ' + f'uuid={self.service.uuid}, ' + f'{self.service.properties!s})' + ) + + +# ----------------------------------------------------------------------------- class Characteristic(Attribute): ''' See Vol 3, Part G - 3.3 CHARACTERISTIC DEFINITION ''' - # Property flags - BROADCAST = 0x01 - READ = 0x02 - WRITE_WITHOUT_RESPONSE = 0x04 - WRITE = 0x08 - NOTIFY = 0x10 - INDICATE = 0X20 - AUTHENTICATED_SIGNED_WRITES = 0X40 - EXTENDED_PROPERTIES = 0X80 - - PROPERTY_NAMES = { - BROADCAST: 'BROADCAST', - READ: 'READ', - WRITE_WITHOUT_RESPONSE: 'WRITE_WITHOUT_RESPONSE', - WRITE: 'WRITE', - NOTIFY: 'NOTIFY', - INDICATE: 'INDICATE', - AUTHENTICATED_SIGNED_WRITES: 'AUTHENTICATED_SIGNED_WRITES', - EXTENDED_PROPERTIES: 'EXTENDED_PROPERTIES' - } - - @staticmethod - def property_name(property): - return Characteristic.PROPERTY_NAMES.get(property, '') - - @staticmethod - def properties_as_string(properties): - return ','.join([ - Characteristic.property_name(p) for p in Characteristic.PROPERTY_NAMES.keys() - if properties & p - ]) - - def __init__(self, uuid, properties, permissions, value = b'', descriptors = []): + uuid: UUID + properties: Characteristic.Properties + + class Properties(enum.IntFlag): + """Property flags""" + + BROADCAST = 0x01 + READ = 0x02 + WRITE_WITHOUT_RESPONSE = 0x04 + WRITE = 0x08 + NOTIFY = 0x10 + INDICATE = 0x20 + AUTHENTICATED_SIGNED_WRITES = 0x40 + EXTENDED_PROPERTIES = 0x80 + + @staticmethod + def from_string(properties_str: str) -> Characteristic.Properties: + property_names: List[str] = [] + for property in Characteristic.Properties: + if property.name is None: + raise TypeError() + property_names.append(property.name) + + def string_to_property(property_string) -> Characteristic.Properties: + for property in zip(Characteristic.Properties, property_names): + if property_string == property[1]: + return property[0] + raise TypeError(f"Unable to convert {property_string} to Property") + + try: + return functools.reduce( + lambda x, y: x | string_to_property(y), + properties_str.split(","), + Characteristic.Properties(0), + ) + except TypeError: + raise TypeError( + f"Characteristic.Properties::from_string() error:\nExpected a string containing any of the keys, separated by commas: {','.join(property_names)}\nGot: {properties_str}" + ) + + # For backwards compatibility these are defined here + # For new code, please use Characteristic.Properties.X + BROADCAST = Properties.BROADCAST + READ = Properties.READ + WRITE_WITHOUT_RESPONSE = Properties.WRITE_WITHOUT_RESPONSE + WRITE = Properties.WRITE + NOTIFY = Properties.NOTIFY + INDICATE = Properties.INDICATE + AUTHENTICATED_SIGNED_WRITES = Properties.AUTHENTICATED_SIGNED_WRITES + EXTENDED_PROPERTIES = Properties.EXTENDED_PROPERTIES + + def __init__( + self, + uuid, + properties: Characteristic.Properties, + permissions, + value=b'', + descriptors: Sequence[Descriptor] = (), + ): super().__init__(uuid, permissions, value) - self.uuid = self.type - self.properties = properties + self.uuid = self.type + self.properties = properties self.descriptors = descriptors def get_descriptor(self, descriptor_type): for descriptor in self.descriptors: - if descriptor.uuid == descriptor_type: + if descriptor.type == descriptor_type: return descriptor + return None + + def has_properties(self, properties: Characteristic.Properties) -> bool: + return self.properties & properties == properties + + def __str__(self): + return ( + f'Characteristic(handle=0x{self.handle:04X}, ' + f'end=0x{self.end_group_handle:04X}, ' + f'uuid={self.uuid}, ' + f'{self.properties!s})' + ) + + +# ----------------------------------------------------------------------------- +class CharacteristicDeclaration(Attribute): + ''' + See Vol 3, Part G - 3.3.1 CHARACTERISTIC DECLARATION + ''' + + characteristic: Characteristic + + def __init__(self, characteristic, value_handle): + declaration_bytes = ( + struct.pack('<BH', characteristic.properties, value_handle) + + characteristic.uuid.to_pdu_bytes() + ) + super().__init__( + GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, Attribute.READABLE, declaration_bytes + ) + self.value_handle = value_handle + self.characteristic = characteristic + def __str__(self): - return f'Characteristic(handle=0x{self.handle:04X}, end=0x{self.end_group_handle:04X}, uuid={self.uuid}, properties={Characteristic.properties_as_string(self.properties)})' + return ( + f'CharacteristicDeclaration(handle=0x{self.handle:04X}, ' + f'value_handle=0x{self.value_handle:04X}, ' + f'uuid={self.characteristic.uuid}, ' + f'{self.characteristic.properties!s})' + ) # ----------------------------------------------------------------------------- @@ -268,6 +411,7 @@ class CharacteristicValue: Characteristic value where reading and/or writing is delegated to functions passed as arguments to the constructor. ''' + def __init__(self, read=None, write=None): self._read = read self._write = write @@ -294,36 +438,57 @@ class CharacteristicAdapter: If the characteristic has a `subscribe` method, it is wrapped with one where the values are decoded before being passed to the subscriber. ''' + def __init__(self, characteristic): self.wrapped_characteristic = characteristic + self.subscribers = {} # Map from subscriber to proxy subscriber - if ( - asyncio.iscoroutinefunction(characteristic.read_value) and - asyncio.iscoroutinefunction(characteristic.write_value) - ): - self.read_value = self.read_decoded_value + if asyncio.iscoroutinefunction( + characteristic.read_value + ) and asyncio.iscoroutinefunction(characteristic.write_value): + self.read_value = self.read_decoded_value self.write_value = self.write_decoded_value else: - self.read_value = self.read_encoded_value + self.read_value = self.read_encoded_value self.write_value = self.write_encoded_value if hasattr(self.wrapped_characteristic, 'subscribe'): self.subscribe = self.wrapped_subscribe + if hasattr(self.wrapped_characteristic, 'unsubscribe'): + self.unsubscribe = self.wrapped_unsubscribe + def __getattr__(self, name): return getattr(self.wrapped_characteristic, name) + def __setattr__(self, name, value): + if name in ( + 'wrapped_characteristic', + 'subscribers', + 'read_value', + 'write_value', + 'subscribe', + 'unsubscribe', + ): + super().__setattr__(name, value) + else: + setattr(self.wrapped_characteristic, name, value) + def read_encoded_value(self, connection): return self.encode_value(self.wrapped_characteristic.read_value(connection)) def write_encoded_value(self, connection, value): - return self.wrapped_characteristic.write_value(connection, self.decode_value(value)) + return self.wrapped_characteristic.write_value( + connection, self.decode_value(value) + ) async def read_decoded_value(self): return self.decode_value(await self.wrapped_characteristic.read_value()) - async def write_decoded_value(self, value): - return await self.wrapped_characteristic.write_value(self.encode_value(value)) + async def write_decoded_value(self, value, with_response=False): + return await self.wrapped_characteristic.write_value( + self.encode_value(value), with_response + ) def encode_value(self, value): return value @@ -332,23 +497,49 @@ class CharacteristicAdapter: return value def wrapped_subscribe(self, subscriber=None): - return self.wrapped_characteristic.subscribe( - None if subscriber is None else lambda value: subscriber(self.decode_value(value)) - ) + if subscriber is not None: + if subscriber in self.subscribers: + # We already have a proxy subscriber + subscriber = self.subscribers[subscriber] + else: + # Create and register a proxy that will decode the value + original_subscriber = subscriber + + def on_change(value): + original_subscriber(self.decode_value(value)) + + self.subscribers[subscriber] = on_change + subscriber = on_change + + return self.wrapped_characteristic.subscribe(subscriber) + + def wrapped_unsubscribe(self, subscriber=None): + if subscriber in self.subscribers: + subscriber = self.subscribers.pop(subscriber) + + return self.wrapped_characteristic.unsubscribe(subscriber) + + def __str__(self): + wrapped = str(self.wrapped_characteristic) + return f'{self.__class__.__name__}({wrapped})' # ----------------------------------------------------------------------------- class DelegatedCharacteristicAdapter(CharacteristicAdapter): - def __init__(self, characteristic, encode, decode): + ''' + Adapter that converts bytes values using an encode and a decode function. + ''' + + def __init__(self, characteristic, encode=None, decode=None): super().__init__(characteristic) self.encode = encode self.decode = decode def encode_value(self, value): - return self.encode(value) + return self.encode(value) if self.encode else value def decode_value(self, value): - return self.decode(value) + return self.decode(value) if self.decode else value # ----------------------------------------------------------------------------- @@ -361,9 +552,10 @@ class PackedCharacteristicAdapter(CharacteristicAdapter): they return/accept a tuple with the same number of elements as is required for the format. ''' - def __init__(self, characteristic, format): + + def __init__(self, characteristic, pack_format): super().__init__(characteristic) - self.struct = struct.Struct(format) + self.struct = struct.Struct(pack_format) def pack(self, *values): return self.struct.pack(*values) @@ -372,7 +564,7 @@ class PackedCharacteristicAdapter(CharacteristicAdapter): return self.struct.unpack(buffer) def encode_value(self, value): - return self.pack(*value if type(value) is tuple else (value,)) + return self.pack(*value if isinstance(value, tuple) else (value,)) def decode_value(self, value): unpacked = self.unpack(value) @@ -385,13 +577,15 @@ class MappedCharacteristicAdapter(PackedCharacteristicAdapter): Adapter that packs/unpacks characteristic values according to a standard Python `struct` format. The adapted `read_value` and `write_value` methods return/accept aa dictionary which - is packed/unpacked according to format, with the arguments extracted from the dictionary - by key, in the same order as they occur in the `keys` parameter. + is packed/unpacked according to format, with the arguments extracted from the + dictionary by key, in the same order as they occur in the `keys` parameter. ''' - def __init__(self, characteristic, format, keys): - super().__init__(characteristic, format) + + def __init__(self, characteristic, pack_format, keys): + super().__init__(characteristic, pack_format) self.keys = keys + # pylint: disable=arguments-differ def pack(self, values): return super().pack(*(values[key] for key in self.keys)) @@ -404,6 +598,7 @@ class UTF8CharacteristicAdapter(CharacteristicAdapter): ''' Adapter that converts strings to/from bytes using UTF-8 encoding ''' + def encode_value(self, value): return value.encode('utf-8') @@ -417,8 +612,20 @@ class Descriptor(Attribute): See Vol 3, Part G - 3.3.3 Characteristic Descriptor Declarations ''' - def __init__(self, descriptor_type, permissions, value = b''): - super().__init__(descriptor_type, permissions, value) - def __str__(self): - return f'Descriptor(handle=0x{self.handle:04X}, type={self.type}, value={self.read_value(None).hex()})' + return ( + f'Descriptor(handle=0x{self.handle:04X}, ' + f'type={self.type}, ' + f'value={self.read_value(None).hex()})' + ) + + +class ClientCharacteristicConfigurationBits(enum.IntFlag): + ''' + See Vol 3, Part G - 3.3.3.3 - Table 3.11 Client Characteristic Configuration bit + field definition + ''' + + DEFAULT = 0x0000 + NOTIFICATION = 0x0001 + INDICATION = 0x0002 diff --git a/bumble/gatt_client.py b/bumble/gatt_client.py index e817e2e..a33039e 100644 --- a/bumble/gatt_client.py +++ b/bumble/gatt_client.py @@ -23,21 +23,49 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import asyncio import logging import struct -from colors import color - -from .core import ProtocolError, TimeoutError -from .hci import * -from .att import * +from datetime import datetime +from typing import List, Optional, Dict, Tuple, Callable, Union, Any + +from pyee import EventEmitter + +from .colors import color +from .hci import HCI_Constant +from .att import ( + ATT_ATTRIBUTE_NOT_FOUND_ERROR, + ATT_ATTRIBUTE_NOT_LONG_ERROR, + ATT_CID, + ATT_DEFAULT_MTU, + ATT_ERROR_RESPONSE, + ATT_INVALID_OFFSET_ERROR, + ATT_PDU, + ATT_RESPONSES, + ATT_Exchange_MTU_Request, + ATT_Find_By_Type_Value_Request, + ATT_Find_Information_Request, + ATT_Handle_Value_Confirmation, + ATT_Read_Blob_Request, + ATT_Read_By_Group_Type_Request, + ATT_Read_By_Type_Request, + ATT_Read_Request, + ATT_Write_Command, + ATT_Write_Request, + ATT_Error, +) +from . import core +from .core import UUID, InvalidStateError, ProtocolError from .gatt import ( + GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR, - GATT_REQUEST_TIMEOUT, GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, + GATT_REQUEST_TIMEOUT, GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, - GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, - Characteristic + GATT_INCLUDE_ATTRIBUTE_TYPE, + Characteristic, + ClientCharacteristicConfigurationBits, ) # ----------------------------------------------------------------------------- @@ -50,38 +78,59 @@ logger = logging.getLogger(__name__) # Proxies # ----------------------------------------------------------------------------- class AttributeProxy(EventEmitter): + client: Client + def __init__(self, client, handle, end_group_handle, attribute_type): EventEmitter.__init__(self) - self.client = client - self.handle = handle + self.client = client + self.handle = handle self.end_group_handle = end_group_handle - self.type = attribute_type + self.type = attribute_type async def read_value(self, no_long_read=False): - return await self.client.read_value(self.handle, no_long_read) + return self.decode_value( + await self.client.read_value(self.handle, no_long_read) + ) async def write_value(self, value, with_response=False): - return await self.client.write_value(self.handle, value, with_response) + return await self.client.write_value( + self.handle, self.encode_value(value), with_response + ) + + def encode_value(self, value): + return value + + def decode_value(self, value_bytes): + return value_bytes def __str__(self): - return f'Attribute(handle=0x{self.handle:04X}, type={self.uuid})' + return f'Attribute(handle=0x{self.handle:04X}, type={self.type})' class ServiceProxy(AttributeProxy): + uuid: UUID + characteristics: List[CharacteristicProxy] + included_services: List[ServiceProxy] + @staticmethod - def from_client(cls, client, service_uuid): - # The service and its characteristics are considered to have already been discovered + def from_client(service_class, client, service_uuid): + # The service and its characteristics are considered to have already been + # discovered services = client.get_services_by_uuid(service_uuid) service = services[0] if services else None - return cls(service) if service else None + return service_class(service) if service else None def __init__(self, client, handle, end_group_handle, uuid, primary=True): - attribute_type = GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE if primary else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE + attribute_type = ( + GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE + if primary + else GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE + ) super().__init__(client, handle, end_group_handle, attribute_type) - self.uuid = uuid + self.uuid = uuid self.characteristics = [] - async def discover_characteristics(self, uuids=[]): + async def discover_characteristics(self, uuids=()): return await self.client.discover_characteristics(uuids, self) def get_characteristics_by_uuid(self, uuid): @@ -92,26 +141,66 @@ class ServiceProxy(AttributeProxy): class CharacteristicProxy(AttributeProxy): - def __init__(self, client, handle, end_group_handle, uuid, properties): + properties: Characteristic.Properties + descriptors: List[DescriptorProxy] + subscribers: Dict[Any, Callable] + + def __init__( + self, + client, + handle, + end_group_handle, + uuid, + properties: int, + ): super().__init__(client, handle, end_group_handle, uuid) - self.uuid = uuid - self.properties = properties - self.descriptors = [] + self.uuid = uuid + self.properties = Characteristic.Properties(properties) + self.descriptors = [] self.descriptors_discovered = False + self.subscribers = {} # Map from subscriber to proxy subscriber def get_descriptor(self, descriptor_type): for descriptor in self.descriptors: if descriptor.type == descriptor_type: return descriptor + return None + async def discover_descriptors(self): return await self.client.discover_descriptors(self) - async def subscribe(self, subscriber=None): - return await self.client.subscribe(self, subscriber) + async def subscribe( + self, subscriber: Optional[Callable] = None, prefer_notify=True + ): + if subscriber is not None: + if subscriber in self.subscribers: + # We already have a proxy subscriber + subscriber = self.subscribers[subscriber] + else: + # Create and register a proxy that will decode the value + original_subscriber = subscriber + + def on_change(value): + original_subscriber(self.decode_value(value)) + + self.subscribers[subscriber] = on_change + subscriber = on_change + + return await self.client.subscribe(self, subscriber, prefer_notify) + + async def unsubscribe(self, subscriber=None): + if subscriber in self.subscribers: + subscriber = self.subscribers.pop(subscriber) + + return await self.client.unsubscribe(self, subscriber) def __str__(self): - return f'Characteristic(handle=0x{self.handle:04X}, uuid={self.uuid}, properties={Characteristic.properties_as_string(self.properties)})' + return ( + f'Characteristic(handle=0x{self.handle:04X}, ' + f'uuid={self.uuid}, ' + f'{self.properties!s})' + ) class DescriptorProxy(AttributeProxy): @@ -126,6 +215,7 @@ class ProfileServiceProxy: ''' Base class for profile-specific service proxies ''' + @classmethod def from_client(cls, client): return ServiceProxy.from_client(cls, client, cls.SERVICE_CLASS.UUID) @@ -135,51 +225,65 @@ class ProfileServiceProxy: # GATT Client # ----------------------------------------------------------------------------- class Client: + services: List[ServiceProxy] + cached_values: Dict[int, Tuple[datetime, bytes]] + def __init__(self, connection): - self.connection = connection - self.mtu = ATT_DEFAULT_MTU - self.mtu_exchange_done = False - self.request_semaphore = asyncio.Semaphore(1) - self.pending_request = None - self.pending_response = None - self.notification_subscribers = {} # Notification subscribers, by attribute handle - self.indication_subscribers = {} # Indication subscribers, by attribute handle - self.services = [] + self.connection = connection + self.mtu_exchange_done = False + self.request_semaphore = asyncio.Semaphore(1) + self.pending_request = None + self.pending_response = None + self.notification_subscribers = ( + {} + ) # Notification subscribers, by attribute handle + self.indication_subscribers = {} # Indication subscribers, by attribute handle + self.services = [] + self.cached_values = {} def send_gatt_pdu(self, pdu): self.connection.send_l2cap_pdu(ATT_CID, pdu) async def send_command(self, command): - logger.debug(f'GATT Command from client: [0x{self.connection.handle:04X}] {command}') + logger.debug( + f'GATT Command from client: [0x{self.connection.handle:04X}] {command}' + ) self.send_gatt_pdu(command.to_bytes()) async def send_request(self, request): - logger.debug(f'GATT Request from client: [0x{self.connection.handle:04X}] {request}') + logger.debug( + f'GATT Request from client: [0x{self.connection.handle:04X}] {request}' + ) # Wait until we can send (only one pending command at a time for the connection) response = None async with self.request_semaphore: - assert(self.pending_request is None) - assert(self.pending_response is None) + assert self.pending_request is None + assert self.pending_response is None # Create a future value to hold the eventual response self.pending_response = asyncio.get_running_loop().create_future() - self.pending_request = request + self.pending_request = request try: self.send_gatt_pdu(request.to_bytes()) - response = await asyncio.wait_for(self.pending_response, GATT_REQUEST_TIMEOUT) - except asyncio.TimeoutError: + response = await asyncio.wait_for( + self.pending_response, GATT_REQUEST_TIMEOUT + ) + except asyncio.TimeoutError as error: logger.warning(color('!!! GATT Request timeout', 'red')) - raise TimeoutError(f'GATT timeout for {request.name}') + raise core.TimeoutError(f'GATT timeout for {request.name}') from error finally: - self.pending_request = None + self.pending_request = None self.pending_response = None return response def send_confirmation(self, confirmation): - logger.debug(f'GATT Confirmation from client: [0x{self.connection.handle:04X}] {confirmation}') + logger.debug( + f'GATT Confirmation from client: [0x{self.connection.handle:04X}] ' + f'{confirmation}' + ) self.send_gatt_pdu(confirmation.to_bytes()) async def request_mtu(self, mtu): @@ -191,31 +295,66 @@ class Client: # We can only send one request per connection if self.mtu_exchange_done: - return + return self.connection.att_mtu # Send the request self.mtu_exchange_done = True - response = await self.send_request(ATT_Exchange_MTU_Request(client_rx_mtu = mtu)) + response = await self.send_request(ATT_Exchange_MTU_Request(client_rx_mtu=mtu)) if response.op_code == ATT_ERROR_RESPONSE: raise ProtocolError( response.error_code, 'att', ATT_PDU.error_name(response.error_code), - response + response, ) - self.mtu = max(ATT_DEFAULT_MTU, response.server_rx_mtu) - return self.mtu + # Compute the final MTU + self.connection.att_mtu = min(mtu, response.server_rx_mtu) + + return self.connection.att_mtu def get_services_by_uuid(self, uuid): return [service for service in self.services if service.uuid == uuid] - def get_characteristics_by_uuid(self, uuid, service = None): + def get_characteristics_by_uuid(self, uuid, service=None): services = [service] if service else self.services - return [c for c in [c for s in services for c in s.characteristics] if c.uuid == uuid] + return [ + c + for c in [c for s in services for c in s.characteristics] + if c.uuid == uuid + ] + + def get_attribute_grouping( + self, attribute_handle: int + ) -> Optional[ + Union[ + ServiceProxy, + Tuple[ServiceProxy, CharacteristicProxy], + Tuple[ServiceProxy, CharacteristicProxy, DescriptorProxy], + ] + ]: + """ + Get the attribute(s) associated with an attribute handle + """ + for service in self.services: + if service.handle == attribute_handle: + return service + if service.handle <= attribute_handle <= service.end_group_handle: + for characteristic in service.characteristics: + if characteristic.handle == attribute_handle: + return (service, characteristic) + if ( + characteristic.handle + <= attribute_handle + <= characteristic.end_group_handle + ): + for descriptor in characteristic.descriptors: + if descriptor.handle == attribute_handle: + return (service, characteristic, descriptor) + return None def on_service_discovered(self, service): - ''' Add a service to the service list if it wasn't already there ''' + '''Add a service to the service list if it wasn't already there''' already_known = False for existing_service in self.services: if existing_service.handle == service.handle: @@ -224,7 +363,7 @@ class Client: if not already_known: self.services.append(service) - async def discover_services(self, uuids = None): + async def discover_services(self, uuids=None) -> List[ServiceProxy]: ''' See Vol 3, Part G - 4.4.1 Discover All Primary Services ''' @@ -233,9 +372,9 @@ class Client: while starting_handle < 0xFFFF: response = await self.send_request( ATT_Read_By_Group_Type_Request( - starting_handle = starting_handle, - ending_handle = 0xFFFF, - attribute_group_type = GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE + starting_handle=starting_handle, + ending_handle=0xFFFF, + attribute_group_type=GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, ) ) if response is None: @@ -246,16 +385,30 @@ class Client: if response.op_code == ATT_ERROR_RESPONSE: if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR: # Unexpected end - logger.waning(f'!!! unexpected error while discovering services: {HCI_Constant.error_name(response.error_code)}') - # TODO raise appropriate exception - return + logger.warning( + '!!! unexpected error while discovering services: ' + f'{HCI_Constant.error_name(response.error_code)}' + ) + raise ATT_Error( + error_code=response.error_code, + message='Unexpected error while discovering services', + ) break - for attribute_handle, end_group_handle, attribute_value in response.attributes: - if attribute_handle < starting_handle or end_group_handle < attribute_handle: + for ( + attribute_handle, + end_group_handle, + attribute_value, + ) in response.attributes: + if ( + attribute_handle < starting_handle + or end_group_handle < attribute_handle + ): # Something's not right - logger.warning(f'bogus handle values: {attribute_handle} {end_group_handle}') - return + logger.warning( + f'bogus handle values: {attribute_handle} {end_group_handle}' + ) + return [] # Create a service proxy for this service service = ServiceProxy( @@ -263,7 +416,7 @@ class Client: attribute_handle, end_group_handle, UUID.from_bytes(attribute_value), - True + True, ) # Filter out returned services based on the given uuids list @@ -288,7 +441,7 @@ class Client: ''' # Force uuid to be a UUID object - if type(uuid) is str: + if isinstance(uuid, str): uuid = UUID(uuid) starting_handle = 0x0001 @@ -296,10 +449,10 @@ class Client: while starting_handle < 0xFFFF: response = await self.send_request( ATT_Find_By_Type_Value_Request( - starting_handle = starting_handle, - ending_handle = 0xFFFF, - attribute_type = GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, - attribute_value = uuid.to_pdu_bytes() + starting_handle=starting_handle, + ending_handle=0xFFFF, + attribute_type=GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, + attribute_value=uuid.to_pdu_bytes(), ) ) if response is None: @@ -310,19 +463,29 @@ class Client: if response.op_code == ATT_ERROR_RESPONSE: if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR: # Unexpected end - logger.waning(f'!!! unexpected error while discovering services: {HCI_Constant.error_name(response.error_code)}') + logger.warning( + '!!! unexpected error while discovering services: ' + f'{HCI_Constant.error_name(response.error_code)}' + ) # TODO raise appropriate exception return break for attribute_handle, end_group_handle in response.handles_information: - if attribute_handle < starting_handle or end_group_handle < attribute_handle: + if ( + attribute_handle < starting_handle + or end_group_handle < attribute_handle + ): # Something's not right - logger.warning(f'bogus handle values: {attribute_handle} {end_group_handle}') + logger.warning( + f'bogus handle values: {attribute_handle} {end_group_handle}' + ) return # Create a service proxy for this service - service = ServiceProxy(self, attribute_handle, end_group_handle, uuid, True) + service = ServiceProxy( + self, attribute_handle, end_group_handle, uuid, True + ) # Add the service to the peer's service list services.append(service) @@ -341,37 +504,97 @@ class Client: return services - async def discover_included_services(self, service): + async def discover_included_services( + self, service: ServiceProxy + ) -> List[ServiceProxy]: ''' See Vol 3, Part G - 4.5.1 Find Included Services ''' - # TODO - return [] - async def discover_characteristics(self, uuids, service): + starting_handle = service.handle + ending_handle = service.end_group_handle + + included_services: List[ServiceProxy] = [] + while starting_handle <= ending_handle: + response = await self.send_request( + ATT_Read_By_Type_Request( + starting_handle=starting_handle, + ending_handle=ending_handle, + attribute_type=GATT_INCLUDE_ATTRIBUTE_TYPE, + ) + ) + if response is None: + # TODO raise appropriate exception + return [] + + # Check if we reached the end of the iteration + if response.op_code == ATT_ERROR_RESPONSE: + if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR: + # Unexpected end + logger.warning( + '!!! unexpected error while discovering included services: ' + f'{HCI_Constant.error_name(response.error_code)}' + ) + raise ATT_Error( + error_code=response.error_code, + message='Unexpected error while discovering included services', + ) + break + + # Stop if for some reason the list was empty + if not response.attributes: + break + + # Process all included services returned in this iteration + for attribute_handle, attribute_value in response.attributes: + if attribute_handle < starting_handle: + # Something's not right + logger.warning(f'bogus handle value: {attribute_handle}') + return [] + + group_starting_handle, group_ending_handle = struct.unpack_from( + '<HH', attribute_value + ) + service_uuid = UUID.from_bytes(attribute_value[4:]) + included_service = ServiceProxy( + self, group_starting_handle, group_ending_handle, service_uuid, True + ) + + included_services.append(included_service) + + # Move on to the next included services + starting_handle = response.attributes[-1][0] + 1 + + service.included_services = included_services + return included_services + + async def discover_characteristics( + self, uuids, service: Optional[ServiceProxy] + ) -> List[CharacteristicProxy]: ''' - See Vol 3, Part G - 4.6.1 Discover All Characteristics of a Service and 4.6.2 Discover Characteristics by UUID + See Vol 3, Part G - 4.6.1 Discover All Characteristics of a Service and 4.6.2 + Discover Characteristics by UUID ''' # Cast the UUIDs type from string to object if needed - uuids = [UUID(uuid) if type(uuid) is str else uuid for uuid in uuids] + uuids = [UUID(uuid) if isinstance(uuid, str) else uuid for uuid in uuids] # Decide which services to discover for services = [service] if service else self.services # Perform characteristic discovery for each service - discovered_characteristics = [] + discovered_characteristics: List[CharacteristicProxy] = [] for service in services: starting_handle = service.handle - ending_handle = service.end_group_handle + ending_handle = service.end_group_handle - characteristics = [] + characteristics: List[CharacteristicProxy] = [] while starting_handle <= ending_handle: response = await self.send_request( ATT_Read_By_Type_Request( - starting_handle = starting_handle, - ending_handle = ending_handle, - attribute_type = GATT_CHARACTERISTIC_ATTRIBUTE_TYPE + starting_handle=starting_handle, + ending_handle=ending_handle, + attribute_type=GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, ) ) if response is None: @@ -382,9 +605,14 @@ class Client: if response.op_code == ATT_ERROR_RESPONSE: if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR: # Unexpected end - logger.warning(f'!!! unexpected error while discovering characteristics: {HCI_Constant.error_name(response.error_code)}') - # TODO raise appropriate exception - return + logger.warning( + '!!! unexpected error while discovering characteristics: ' + f'{HCI_Constant.error_name(response.error_code)}' + ) + raise ATT_Error( + error_code=response.error_code, + message='Unexpected error while discovering characteristics', + ) break # Stop if for some reason the list was empty @@ -400,7 +628,9 @@ class Client: properties, handle = struct.unpack_from('<BH', attribute_value) characteristic_uuid = UUID.from_bytes(attribute_value[3:]) - characteristic = CharacteristicProxy(self, handle, 0, characteristic_uuid, properties) + characteristic = CharacteristicProxy( + self, handle, 0, characteristic_uuid, properties + ) # Set the previous characteristic's end handle if characteristics: @@ -416,31 +646,37 @@ class Client: characteristics[-1].end_group_handle = service.end_group_handle # Set the service's characteristics - characteristics = [c for c in characteristics if not uuids or c.uuid in uuids] + characteristics = [ + c for c in characteristics if not uuids or c.uuid in uuids + ] service.characteristics = characteristics discovered_characteristics.extend(characteristics) return discovered_characteristics - async def discover_descriptors(self, characteristic = None, start_handle = None, end_handle = None): + async def discover_descriptors( + self, + characteristic: Optional[CharacteristicProxy] = None, + start_handle=None, + end_handle=None, + ) -> List[DescriptorProxy]: ''' See Vol 3, Part G - 4.7.1 Discover All Characteristic Descriptors ''' if characteristic: starting_handle = characteristic.handle + 1 - ending_handle = characteristic.end_group_handle + ending_handle = characteristic.end_group_handle elif start_handle and end_handle: starting_handle = start_handle - ending_handle = end_handle + ending_handle = end_handle else: return [] - descriptors = [] + descriptors: List[DescriptorProxy] = [] while starting_handle <= ending_handle: response = await self.send_request( ATT_Find_Information_Request( - starting_handle = starting_handle, - ending_handle = ending_handle + starting_handle=starting_handle, ending_handle=ending_handle ) ) if response is None: @@ -451,7 +687,10 @@ class Client: if response.op_code == ATT_ERROR_RESPONSE: if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR: # Unexpected end - logger.warning(f'!!! unexpected error while discovering descriptors: {HCI_Constant.error_name(response.error_code)}') + logger.warning( + '!!! unexpected error while discovering descriptors: ' + f'{HCI_Constant.error_name(response.error_code)}' + ) # TODO raise appropriate exception return [] break @@ -467,7 +706,9 @@ class Client: logger.warning(f'bogus handle value: {attribute_handle}') return [] - descriptor = DescriptorProxy(self, attribute_handle, UUID.from_bytes(attribute_uuid)) + descriptor = DescriptorProxy( + self, attribute_handle, UUID.from_bytes(attribute_uuid) + ) descriptors.append(descriptor) # TODO: read descriptor value @@ -485,13 +726,12 @@ class Client: Discover all attributes, regardless of type ''' starting_handle = 0x0001 - ending_handle = 0xFFFF + ending_handle = 0xFFFF attributes = [] while True: response = await self.send_request( ATT_Find_Information_Request( - starting_handle = starting_handle, - ending_handle = ending_handle + starting_handle=starting_handle, ending_handle=ending_handle ) ) if response is None: @@ -501,7 +741,10 @@ class Client: if response.op_code == ATT_ERROR_RESPONSE: if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR: # Unexpected end - logger.warning(f'!!! unexpected error while discovering attributes: {HCI_Constant.error_name(response.error_code)}') + logger.warning( + '!!! unexpected error while discovering attributes: ' + f'{HCI_Constant.error_name(response.error_code)}' + ) return [] break @@ -511,7 +754,9 @@ class Client: logger.warning(f'bogus handle value: {attribute_handle}') return [] - attribute = AttributeProxy(self, attribute_handle, 0, UUID.from_bytes(attribute_uuid)) + attribute = AttributeProxy( + self, attribute_handle, 0, UUID.from_bytes(attribute_uuid) + ) attributes.append(attribute) # Move on to the next attributes @@ -519,35 +764,86 @@ class Client: return attributes - async def subscribe(self, characteristic, subscriber=None): - # If we haven't already discovered the descriptors for this characteristic, do it now + async def subscribe(self, characteristic, subscriber=None, prefer_notify=True): + # If we haven't already discovered the descriptors for this characteristic, + # do it now if not characteristic.descriptors_discovered: await self.discover_descriptors(characteristic) # Look for the CCCD descriptor - cccd = characteristic.get_descriptor(GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR) + cccd = characteristic.get_descriptor( + GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR + ) if not cccd: logger.warning('subscribing to characteristic with no CCCD descriptor') return - # Set the subscription bits and select the subscriber set - bits = 0 - subscriber_sets = [] - if characteristic.properties & Characteristic.NOTIFY: - bits |= 0x0001 - subscriber_sets.append(self.notification_subscribers.setdefault(characteristic.handle, set())) - if characteristic.properties & Characteristic.INDICATE: - bits |= 0x0002 - subscriber_sets.append(self.indication_subscribers.setdefault(characteristic.handle, set())) + if ( + characteristic.properties & Characteristic.Properties.NOTIFY + and characteristic.properties & Characteristic.Properties.INDICATE + ): + if prefer_notify: + bits = ClientCharacteristicConfigurationBits.NOTIFICATION + subscribers = self.notification_subscribers + else: + bits = ClientCharacteristicConfigurationBits.INDICATION + subscribers = self.indication_subscribers + elif characteristic.properties & Characteristic.Properties.NOTIFY: + bits = ClientCharacteristicConfigurationBits.NOTIFICATION + subscribers = self.notification_subscribers + elif characteristic.properties & Characteristic.Properties.INDICATE: + bits = ClientCharacteristicConfigurationBits.INDICATION + subscribers = self.indication_subscribers + else: + raise InvalidStateError("characteristic is not notify or indicate") # Add subscribers to the sets - for subscriber_set in subscriber_sets: - if subscriber is not None: - subscriber_set.add(subscriber) - subscriber_set.add(lambda value: characteristic.emit('update', self.connection, value)) + subscriber_set = subscribers.setdefault(characteristic.handle, set()) + if subscriber is not None: + subscriber_set.add(subscriber) + # Add the characteristic as a subscriber, which will result in the + # characteristic emitting an 'update' event when a notification or indication + # is received + subscriber_set.add(characteristic) await self.write_value(cccd, struct.pack('<H', bits), with_response=True) + async def unsubscribe(self, characteristic, subscriber=None): + # If we haven't already discovered the descriptors for this characteristic, + # do it now + if not characteristic.descriptors_discovered: + await self.discover_descriptors(characteristic) + + # Look for the CCCD descriptor + cccd = characteristic.get_descriptor( + GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR + ) + if not cccd: + logger.warning('unsubscribing from characteristic with no CCCD descriptor') + return + + if subscriber is not None: + # Remove matching subscriber from subscriber sets + for subscriber_set in ( + self.notification_subscribers, + self.indication_subscribers, + ): + subscribers = subscriber_set.get(characteristic.handle, []) + if subscriber in subscribers: + subscribers.remove(subscriber) + + # Cleanup if we removed the last one + if not subscribers: + del subscriber_set[characteristic.handle] + else: + # Remove all subscribers for this attribute from the sets! + self.notification_subscribers.pop(characteristic.handle, None) + self.indication_subscribers.pop(characteristic.handle, None) + + if not self.notification_subscribers and not self.indication_subscribers: + # No more subscribers left + await self.write_value(cccd, b'\x00\x00', with_response=True) + async def read_value(self, attribute, no_long_read=False): ''' See Vol 3, Part G - 4.8.1 Read Characteristic Value @@ -556,8 +852,10 @@ class Client: ''' # Send a request to read - attribute_handle = attribute if type(attribute) is int else attribute.handle - response = await self.send_request(ATT_Read_Request(attribute_handle = attribute_handle)) + attribute_handle = attribute if isinstance(attribute, int) else attribute.handle + response = await self.send_request( + ATT_Read_Request(attribute_handle=attribute_handle) + ) if response is None: raise TimeoutError('read timeout') if response.op_code == ATT_ERROR_RESPONSE: @@ -565,39 +863,45 @@ class Client: response.error_code, 'att', ATT_PDU.error_name(response.error_code), - response + response, ) # If the value is the max size for the MTU, try to read more unless the caller # specifically asked not to do that attribute_value = response.attribute_value - if not no_long_read and len(attribute_value) == self.mtu - 1: + if not no_long_read and len(attribute_value) == self.connection.att_mtu - 1: logger.debug('using READ BLOB to get the rest of the value') offset = len(attribute_value) while True: response = await self.send_request( - ATT_Read_Blob_Request(attribute_handle = attribute_handle, value_offset = offset) + ATT_Read_Blob_Request( + attribute_handle=attribute_handle, value_offset=offset + ) ) if response is None: raise TimeoutError('read timeout') if response.op_code == ATT_ERROR_RESPONSE: - if response.error_code == ATT_ATTRIBUTE_NOT_LONG_ERROR or response.error_code == ATT_INVALID_OFFSET_ERROR: + if response.error_code in ( + ATT_ATTRIBUTE_NOT_LONG_ERROR, + ATT_INVALID_OFFSET_ERROR, + ): break raise ProtocolError( response.error_code, 'att', ATT_PDU.error_name(response.error_code), - response + response, ) part = response.part_attribute_value attribute_value += part - if len(part) < self.mtu - 1: + if len(part) < self.connection.att_mtu - 1: break offset += len(part) + self.cache_value(attribute_handle, attribute_value) # Return the value as bytes return attribute_value @@ -608,18 +912,18 @@ class Client: if service is None: starting_handle = 0x0001 - ending_handle = 0xFFFF + ending_handle = 0xFFFF else: starting_handle = service.handle - ending_handle = service.end_group_handle + ending_handle = service.end_group_handle characteristics_values = [] while starting_handle <= ending_handle: response = await self.send_request( ATT_Read_By_Type_Request( - starting_handle = starting_handle, - ending_handle = ending_handle, - attribute_type = uuid + starting_handle=starting_handle, + ending_handle=ending_handle, + attribute_type=uuid, ) ) if response is None: @@ -630,7 +934,10 @@ class Client: if response.op_code == ATT_ERROR_RESPONSE: if response.error_code != ATT_ATTRIBUTE_NOT_FOUND_ERROR: # Unexpected end - logger.warning(f'!!! unexpected error while reading characteristics: {HCI_Constant.error_name(response.error_code)}') + logger.warning( + '!!! unexpected error while reading characteristics: ' + f'{HCI_Constant.error_name(response.error_code)}' + ) # TODO raise appropriate exception return [] break @@ -655,47 +962,54 @@ class Client: async def write_value(self, attribute, value, with_response=False): ''' - See Vol 3, Part G - 4.9.1 Write Without Response & 4.9.3 Write Characteristic Value + See Vol 3, Part G - 4.9.1 Write Without Response & 4.9.3 Write Characteristic + Value `attribute` can be an Attribute object, or a handle value ''' # Send a request or command to write - attribute_handle = attribute if type(attribute) is int else attribute.handle + attribute_handle = attribute if isinstance(attribute, int) else attribute.handle if with_response: response = await self.send_request( ATT_Write_Request( - attribute_handle = attribute_handle, - attribute_value = value + attribute_handle=attribute_handle, attribute_value=value ) ) if response.op_code == ATT_ERROR_RESPONSE: raise ProtocolError( response.error_code, 'att', - ATT_PDU.error_name(response.error_code), response + ATT_PDU.error_name(response.error_code), + response, ) else: await self.send_command( ATT_Write_Command( - attribute_handle = attribute_handle, - attribute_value = value + attribute_handle=attribute_handle, attribute_value=value ) ) def on_gatt_pdu(self, att_pdu): - logger.debug(f'GATT Response to client: [0x{self.connection.handle:04X}] {att_pdu}') + logger.debug( + f'GATT Response to client: [0x{self.connection.handle:04X}] {att_pdu}' + ) if att_pdu.op_code in ATT_RESPONSES: if self.pending_request is None: # Not expected! logger.warning('!!! unexpected response, there is no pending request') return - # Sanity check: the response should match the pending request unless it is an error response + # Sanity check: the response should match the pending request unless it is + # an error response if att_pdu.op_code != ATT_ERROR_RESPONSE: - expected_response_name = self.pending_request.name.replace('_REQUEST', '_RESPONSE') + expected_response_name = self.pending_request.name.replace( + '_REQUEST', '_RESPONSE' + ) if att_pdu.name != expected_response_name: - logger.warning(f'!!! mismatched response: expected {expected_response_name}') + logger.warning( + f'!!! mismatched response: expected {expected_response_name}' + ) return # Return the response to the coroutine that is waiting for it @@ -706,23 +1020,48 @@ class Client: if handler is not None: handler(att_pdu) else: - logger.warning(f'{color(f"--- Ignoring GATT Response from [0x{self.connection.handle:04X}]:", "red")} {att_pdu}') + logger.warning( + color( + '--- Ignoring GATT Response from ' + f'[0x{self.connection.handle:04X}]: ', + 'red', + ) + + str(att_pdu) + ) def on_att_handle_value_notification(self, notification): # Call all subscribers - subscribers = self.notification_subscribers.get(notification.attribute_handle, []) + subscribers = self.notification_subscribers.get( + notification.attribute_handle, [] + ) if not subscribers: logger.warning('!!! received notification with no subscriber') + + self.cache_value(notification.attribute_handle, notification.attribute_value) for subscriber in subscribers: - subscriber(notification.attribute_value) + if callable(subscriber): + subscriber(notification.attribute_value) + else: + subscriber.emit('update', notification.attribute_value) def on_att_handle_value_indication(self, indication): # Call all subscribers subscribers = self.indication_subscribers.get(indication.attribute_handle, []) if not subscribers: logger.warning('!!! received indication with no subscriber') + + self.cache_value(indication.attribute_handle, indication.attribute_value) for subscriber in subscribers: - subscriber(indication.attribute_value) + if callable(subscriber): + subscriber(indication.attribute_value) + else: + subscriber.emit('update', indication.attribute_value) # Confirm that we received the indication self.send_confirmation(ATT_Handle_Value_Confirmation()) + + def cache_value(self, attribute_handle: int, value: bytes): + self.cached_values[attribute_handle] = ( + datetime.now(), + value, + ) diff --git a/bumble/gatt_server.py b/bumble/gatt_server.py index 3afcecc..3624905 100644 --- a/bumble/gatt_server.py +++ b/bumble/gatt_server.py @@ -26,13 +26,53 @@ import asyncio import logging from collections import defaultdict +import struct +from typing import List, Tuple, Optional, TypeVar, Type from pyee import EventEmitter -from colors import color -from .core import * -from .hci import * -from .att import * -from .gatt import * +from .colors import color +from .core import UUID +from .att import ( + ATT_ATTRIBUTE_NOT_FOUND_ERROR, + ATT_ATTRIBUTE_NOT_LONG_ERROR, + ATT_CID, + ATT_DEFAULT_MTU, + ATT_INVALID_ATTRIBUTE_LENGTH_ERROR, + ATT_INVALID_HANDLE_ERROR, + ATT_INVALID_OFFSET_ERROR, + ATT_REQUEST_NOT_SUPPORTED_ERROR, + ATT_REQUESTS, + ATT_UNLIKELY_ERROR_ERROR, + ATT_UNSUPPORTED_GROUP_TYPE_ERROR, + ATT_Error, + ATT_Error_Response, + ATT_Exchange_MTU_Response, + ATT_Find_By_Type_Value_Response, + ATT_Find_Information_Response, + ATT_Handle_Value_Indication, + ATT_Handle_Value_Notification, + ATT_Read_Blob_Response, + ATT_Read_By_Group_Type_Response, + ATT_Read_By_Type_Response, + ATT_Read_Response, + ATT_Write_Response, + Attribute, +) +from .gatt import ( + GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, + GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR, + GATT_MAX_ATTRIBUTE_VALUE_SIZE, + GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, + GATT_REQUEST_TIMEOUT, + GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, + Characteristic, + CharacteristicDeclaration, + CharacteristicValue, + IncludedServiceDeclaration, + Descriptor, + Service, +) + # ----------------------------------------------------------------------------- # Logging @@ -41,26 +81,49 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- +# Constants +# ----------------------------------------------------------------------------- +GATT_SERVER_DEFAULT_MAX_MTU = 517 + + +# ----------------------------------------------------------------------------- # GATT Server # ----------------------------------------------------------------------------- class Server(EventEmitter): + attributes: List[Attribute] + def __init__(self, device): super().__init__() - self.device = device - self.attributes = [] # Attributes, ordered by increasing handle values - self.attributes_by_handle = {} # Map for fast attribute access by handle - self.max_mtu = 23 # FIXME: 517 # The max MTU we're willing to negotiate - self.subscribers = {} # Map of subscriber states by connection handle and attribute handle - self.mtus = {} # Map of ATT MTU values by connection handle + self.device = device + self.services = [] + self.attributes = [] # Attributes, ordered by increasing handle values + self.attributes_by_handle = {} # Map for fast attribute access by handle + self.max_mtu = ( + GATT_SERVER_DEFAULT_MAX_MTU # The max MTU we're willing to negotiate + ) + self.subscribers = ( + {} + ) # Map of subscriber states by connection handle and attribute handle self.indication_semaphores = defaultdict(lambda: asyncio.Semaphore(1)) self.pending_confirmations = defaultdict(lambda: None) + def __str__(self): + return "\n".join(map(str, self.attributes)) + def send_gatt_pdu(self, connection_handle, pdu): self.device.send_l2cap_pdu(connection_handle, ATT_CID, pdu) def next_handle(self): return 1 + len(self.attributes) + def get_advertising_service_data(self): + return { + attribute: data + for attribute in self.attributes + if isinstance(attribute, Service) + and (data := attribute.get_advertising_data()) + } + def get_attribute(self, handle): attribute = self.attributes_by_handle.get(handle) if attribute: @@ -74,32 +137,107 @@ class Server(EventEmitter): return attribute return None + AttributeGroupType = TypeVar('AttributeGroupType', Service, Characteristic) + + def get_attribute_group( + self, handle: int, group_type: Type[AttributeGroupType] + ) -> Optional[AttributeGroupType]: + return next( + ( + attribute + for attribute in self.attributes + if isinstance(attribute, group_type) + and attribute.handle <= handle <= attribute.end_group_handle + ), + None, + ) + + def get_service_attribute(self, service_uuid: UUID) -> Optional[Service]: + return next( + ( + attribute + for attribute in self.attributes + if attribute.type == GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE + and isinstance(attribute, Service) + and attribute.uuid == service_uuid + ), + None, + ) + + def get_characteristic_attributes( + self, service_uuid: UUID, characteristic_uuid: UUID + ) -> Optional[Tuple[CharacteristicDeclaration, Characteristic]]: + service_handle = self.get_service_attribute(service_uuid) + if not service_handle: + return None + + return next( + ( + (attribute, self.get_attribute(attribute.characteristic.handle)) + for attribute in map( + self.get_attribute, + range(service_handle.handle, service_handle.end_group_handle + 1), + ) + if attribute.type == GATT_CHARACTERISTIC_ATTRIBUTE_TYPE + and attribute.characteristic.uuid == characteristic_uuid + ), + None, + ) + + def get_descriptor_attribute( + self, service_uuid: UUID, characteristic_uuid: UUID, descriptor_uuid: UUID + ) -> Optional[Descriptor]: + characteristics = self.get_characteristic_attributes( + service_uuid, characteristic_uuid + ) + if not characteristics: + return None + + (_, characteristic_value) = characteristics + + return next( + ( + attribute + for attribute in map( + self.get_attribute, + range( + characteristic_value.handle + 1, + characteristic_value.end_group_handle + 1, + ), + ) + if attribute.type == descriptor_uuid + ), + None, + ) + def add_attribute(self, attribute): # Assign a handle to this attribute attribute.handle = self.next_handle() - attribute.end_group_handle = attribute.handle # TODO: keep track of descriptors in the group + attribute.end_group_handle = ( + attribute.handle + ) # TODO: keep track of descriptors in the group # Add this attribute to the list self.attributes.append(attribute) - def add_service(self, service): + def add_service(self, service: Service): # Add the service attribute to the DB self.add_attribute(service) - # TODO: add included services + # Add all included service + for included_service in service.included_services: + # Not registered yet, register the included service first. + if included_service not in self.services: + self.add_service(included_service) + # TODO: Handle circular service reference + include_declaration = IncludedServiceDeclaration(included_service) + self.add_attribute(include_declaration) # Add all characteristics for characteristic in service.characteristics: - # Add a Characteristic Declaration (Vol 3, Part G - 3.3.1 Characteristic Declaration) - declaration_bytes = struct.pack( - '<BH', - characteristic.properties, - self.next_handle() + 1, # The value will be the next attribute after this declaration - ) + characteristic.uuid.to_pdu_bytes() - characteristic_declaration = Attribute( - GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, - Attribute.READABLE, - declaration_bytes + # Add a Characteristic Declaration + characteristic_declaration = CharacteristicDeclaration( + characteristic, self.next_handle() + 1 ) self.add_attribute(characteristic_declaration) @@ -113,17 +251,29 @@ class Server(EventEmitter): # If the characteristic supports subscriptions, add a CCCD descriptor # unless there is one already if ( - characteristic.properties & (Characteristic.NOTIFY | Characteristic.INDICATE) and - characteristic.get_descriptor(GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR) is None + characteristic.properties + & ( + Characteristic.Properties.NOTIFY + | Characteristic.Properties.INDICATE + ) + and characteristic.get_descriptor( + GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR + ) + is None ): self.add_attribute( + # pylint: disable=line-too-long Descriptor( GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR, Attribute.READABLE | Attribute.WRITEABLE, CharacteristicValue( - read=lambda connection, characteristic=characteristic: self.read_cccd(connection, characteristic), - write=lambda connection, value, characteristic=characteristic: self.write_cccd(connection, characteristic, value) - ) + read=lambda connection, characteristic=characteristic: self.read_cccd( + connection, characteristic + ), + write=lambda connection, value, characteristic=characteristic: self.write_cccd( + connection, characteristic, value + ), + ), ) ) @@ -133,6 +283,7 @@ class Server(EventEmitter): # Update the service group end service.end_group_handle = self.attributes[-1].handle + self.services.append(service) def add_services(self, services): for service in services: @@ -150,26 +301,39 @@ class Server(EventEmitter): return cccd or bytes([0, 0]) def write_cccd(self, connection, characteristic, value): - logger.debug(f'Subscription update for connection={connection.handle:04X}, handle={characteristic.handle:04X}: {value.hex()}') + logger.debug( + f'Subscription update for connection=0x{connection.handle:04X}, ' + f'handle=0x{characteristic.handle:04X}: {value.hex()}' + ) # Sanity check if len(value) != 2: - logger.warn('CCCD value not 2 bytes long') + logger.warning('CCCD value not 2 bytes long') return cccds = self.subscribers.setdefault(connection.handle, {}) cccds[characteristic.handle] = value logger.debug(f'CCCDs: {cccds}') - notify_enabled = (value[0] & 0x01 != 0) - indicate_enabled = (value[0] & 0x02 != 0) - characteristic.emit('subscription', connection, notify_enabled, indicate_enabled) - self.emit('characteristic_subscription', connection, characteristic, notify_enabled, indicate_enabled) + notify_enabled = value[0] & 0x01 != 0 + indicate_enabled = value[0] & 0x02 != 0 + characteristic.emit( + 'subscription', connection, notify_enabled, indicate_enabled + ) + self.emit( + 'characteristic_subscription', + connection, + characteristic, + notify_enabled, + indicate_enabled, + ) def send_response(self, connection, response): - logger.debug(f'GATT Response from server: [0x{connection.handle:04X}] {response}') + logger.debug( + f'GATT Response from server: [0x{connection.handle:04X}] {response}' + ) self.send_gatt_pdu(connection.handle, response.to_bytes()) - async def notify_subscriber(self, connection, attribute, force=False): + async def notify_subscriber(self, connection, attribute, value=None, force=False): # Check if there's a subscriber if not force: subscribers = self.subscribers.get(connection.handle) @@ -178,47 +342,35 @@ class Server(EventEmitter): return cccd = subscribers.get(attribute.handle) if not cccd: - logger.debug(f'not notifying, no subscribers for handle {attribute.handle:04X}') + logger.debug( + f'not notifying, no subscribers for handle {attribute.handle:04X}' + ) return if len(cccd) != 2 or (cccd[0] & 0x01 == 0): logger.debug(f'not notifying, cccd={cccd.hex()}') return - # Get the value - value = attribute.read_value(connection) + # Get or encode the value + value = ( + attribute.read_value(connection) + if value is None + else attribute.encode_value(value) + ) # Truncate if needed - mtu = self.get_mtu(connection) - if len(value) > mtu - 3: - value = value[:mtu - 3] + if len(value) > connection.att_mtu - 3: + value = value[: connection.att_mtu - 3] # Notify notification = ATT_Handle_Value_Notification( - attribute_handle = attribute.handle, - attribute_value = value + attribute_handle=attribute.handle, attribute_value=value ) - logger.debug(f'GATT Notify from server: [0x{connection.handle:04X}] {notification}') - self.send_gatt_pdu(connection.handle, notification.to_bytes()) - - async def notify_subscribers(self, attribute, force=False): - # Get all the connections for which there's at least one subscription - connections = [ - connection for connection in [ - self.device.lookup_connection(connection_handle) - for (connection_handle, subscribers) in self.subscribers.items() - if force or subscribers.get(attribute.handle) - ] - if connection is not None - ] - - # Notify for each connection - if connections: - await asyncio.wait([ - self.notify_subscriber(connection, attribute, force) - for connection in connections - ]) + logger.debug( + f'GATT Notify from server: [0x{connection.handle:04X}] {notification}' + ) + self.send_gatt_pdu(connection.handle, bytes(notification)) - async def indicate_subscriber(self, connection, attribute, force=False): + async def indicate_subscriber(self, connection, attribute, value=None, force=False): # Check if there's a subscriber if not force: subscribers = self.subscribers.get(connection.handle) @@ -227,64 +379,84 @@ class Server(EventEmitter): return cccd = subscribers.get(attribute.handle) if not cccd: - logger.debug(f'not indicating, no subscribers for handle {attribute.handle:04X}') + logger.debug( + f'not indicating, no subscribers for handle {attribute.handle:04X}' + ) return if len(cccd) != 2 or (cccd[0] & 0x02 == 0): logger.debug(f'not indicating, cccd={cccd.hex()}') return - # Get the value - value = attribute.read_value(connection) + # Get or encode the value + value = ( + attribute.read_value(connection) + if value is None + else attribute.encode_value(value) + ) # Truncate if needed - mtu = self.get_mtu(connection) - if len(value) > mtu - 3: - value = value[:mtu - 3] + if len(value) > connection.att_mtu - 3: + value = value[: connection.att_mtu - 3] # Indicate indication = ATT_Handle_Value_Indication( - attribute_handle = attribute.handle, - attribute_value = value + attribute_handle=attribute.handle, attribute_value=value + ) + logger.debug( + f'GATT Indicate from server: [0x{connection.handle:04X}] {indication}' ) - logger.debug(f'GATT Indicate from server: [0x{connection.handle:04X}] {indication}') # Wait until we can send (only one pending indication at a time per connection) async with self.indication_semaphores[connection.handle]: - assert(self.pending_confirmations[connection.handle] is None) + assert self.pending_confirmations[connection.handle] is None # Create a future value to hold the eventual response - self.pending_confirmations[connection.handle] = asyncio.get_running_loop().create_future() + self.pending_confirmations[ + connection.handle + ] = asyncio.get_running_loop().create_future() try: self.send_gatt_pdu(connection.handle, indication.to_bytes()) - await asyncio.wait_for(self.pending_confirmations[connection.handle], GATT_REQUEST_TIMEOUT) - except asyncio.TimeoutError: + await asyncio.wait_for( + self.pending_confirmations[connection.handle], GATT_REQUEST_TIMEOUT + ) + except asyncio.TimeoutError as error: logger.warning(color('!!! GATT Indicate timeout', 'red')) - raise TimeoutError(f'GATT timeout for {indication.name}') + raise TimeoutError(f'GATT timeout for {indication.name}') from error finally: self.pending_confirmations[connection.handle] = None - async def indicate_subscribers(self, attribute): + async def notify_or_indicate_subscribers( + self, indicate, attribute, value=None, force=False + ): # Get all the connections for which there's at least one subscription connections = [ - connection for connection in [ + connection + for connection in [ self.device.lookup_connection(connection_handle) for (connection_handle, subscribers) in self.subscribers.items() - if subscribers.get(attribute.handle) + if force or subscribers.get(attribute.handle) ] if connection is not None ] - # Indicate for each connection + # Indicate or notify for each connection if connections: - await asyncio.wait([ - self.indicate_subscriber(connection, attribute) - for connection in connections - ]) + coroutine = self.indicate_subscriber if indicate else self.notify_subscriber + await asyncio.wait( + [ + asyncio.create_task(coroutine(connection, attribute, value, force)) + for connection in connections + ] + ) + + async def notify_subscribers(self, attribute, value=None, force=False): + return await self.notify_or_indicate_subscribers(False, attribute, value, force) + + async def indicate_subscribers(self, attribute, value=None, force=False): + return await self.notify_or_indicate_subscribers(True, attribute, value, force) def on_disconnection(self, connection): - if connection.handle in self.mtus: - del self.mtus[connection.handle] if connection.handle in self.subscribers: del self.subscribers[connection.handle] if connection.handle in self.indication_semaphores: @@ -302,17 +474,17 @@ class Server(EventEmitter): except ATT_Error as error: logger.debug(f'normal exception returned by handler: {error}') response = ATT_Error_Response( - request_opcode_in_error = att_pdu.op_code, - attribute_handle_in_error = error.att_handle, - error_code = error.error_code + request_opcode_in_error=att_pdu.op_code, + attribute_handle_in_error=error.att_handle, + error_code=error.error_code, ) self.send_response(connection, response) except Exception as error: logger.warning(f'{color("!!! Exception in handler:", "red")} {error}') response = ATT_Error_Response( - request_opcode_in_error = att_pdu.op_code, - attribute_handle_in_error = 0x0000, - error_code = ATT_UNLIKELY_ERROR_ERROR + request_opcode_in_error=att_pdu.op_code, + attribute_handle_in_error=0x0000, + error_code=ATT_UNLIKELY_ERROR_ERROR, ) self.send_response(connection, response) raise error @@ -323,10 +495,13 @@ class Server(EventEmitter): self.on_att_request(connection, att_pdu) else: # Just ignore - logger.warning(f'{color("--- Ignoring GATT Request from [0x{connection.handle:04X}]:", "red")} {att_pdu}') - - def get_mtu(self, connection): - return self.mtus.get(connection.handle, ATT_DEFAULT_MTU) + logger.warning( + color( + f'--- Ignoring GATT Request from [0x{connection.handle:04X}]: ', + 'red', + ) + + str(att_pdu) + ) ####################################################### # ATT handlers @@ -335,11 +510,16 @@ class Server(EventEmitter): ''' Handler for requests without a more specific handler ''' - logger.warning(f'{color(f"--- Unsupported ATT Request from [0x{connection.handle:04X}]:", "red")} {pdu}') + logger.warning( + color( + f'--- Unsupported ATT Request from [0x{connection.handle:04X}]: ', 'red' + ) + + str(pdu) + ) response = ATT_Error_Response( - request_opcode_in_error = pdu.op_code, - attribute_handle_in_error = 0x0000, - error_code = ATT_REQUEST_NOT_SUPPORTED_ERROR + request_opcode_in_error=pdu.op_code, + attribute_handle_in_error=0x0000, + error_code=ATT_REQUEST_NOT_SUPPORTED_ERROR, ) self.send_response(connection, response) @@ -347,12 +527,18 @@ class Server(EventEmitter): ''' See Bluetooth spec Vol 3, Part F - 3.4.2.1 Exchange MTU Request ''' - mtu = max(ATT_DEFAULT_MTU, min(self.max_mtu, request.client_rx_mtu)) - self.mtus[connection.handle] = mtu - self.send_response(connection, ATT_Exchange_MTU_Response(server_rx_mtu = mtu)) + self.send_response( + connection, ATT_Exchange_MTU_Response(server_rx_mtu=self.max_mtu) + ) - # Notify the device - self.device.on_connection_att_mtu_update(connection.handle, mtu) + # Compute the final MTU + if request.client_rx_mtu >= ATT_DEFAULT_MTU: + mtu = min(self.max_mtu, request.client_rx_mtu) + + # Notify the device + self.device.on_connection_att_mtu_update(connection.handle, mtu) + else: + logger.warning('invalid client_rx_mtu received, MTU not changed') def on_att_find_information_request(self, connection, request): ''' @@ -360,25 +546,30 @@ class Server(EventEmitter): ''' # Check the request parameters - if request.starting_handle == 0 or request.starting_handle > request.ending_handle: - self.send_response(connection, ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.starting_handle, - error_code = ATT_INVALID_HANDLE_ERROR - )) + if ( + request.starting_handle == 0 + or request.starting_handle > request.ending_handle + ): + self.send_response( + connection, + ATT_Error_Response( + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.starting_handle, + error_code=ATT_INVALID_HANDLE_ERROR, + ), + ) return # Build list of returned attributes - pdu_space_available = self.get_mtu(connection) - 2 + pdu_space_available = connection.att_mtu - 2 attributes = [] uuid_size = 0 for attribute in ( - attribute for attribute in self.attributes if - attribute.handle >= request.starting_handle and - attribute.handle <= request.ending_handle + attribute + for attribute in self.attributes + if attribute.handle >= request.starting_handle + and attribute.handle <= request.ending_handle ): - # TODO: check permissions - this_uuid_size = len(attribute.type.to_pdu_bytes()) if attributes: @@ -402,14 +593,14 @@ class Server(EventEmitter): for attribute in attributes ] response = ATT_Find_Information_Response( - format = 1 if len(attributes[0].type.to_pdu_bytes()) == 2 else 2, - information_data = b''.join(information_data_list) + format=1 if len(attributes[0].type.to_pdu_bytes()) == 2 else 2, + information_data=b''.join(information_data_list), ) else: response = ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.starting_handle, - error_code = ATT_ATTRIBUTE_NOT_FOUND_ERROR + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.starting_handle, + error_code=ATT_ATTRIBUTE_NOT_FOUND_ERROR, ) self.send_response(connection, response) @@ -420,15 +611,16 @@ class Server(EventEmitter): ''' # Build list of returned attributes - pdu_space_available = self.get_mtu(connection) - 2 + pdu_space_available = connection.att_mtu - 2 attributes = [] for attribute in ( - attribute for attribute in self.attributes if - attribute.handle >= request.starting_handle and - attribute.handle <= request.ending_handle and - attribute.type == request.attribute_type and - attribute.read_value(connection) == request.attribute_value and - pdu_space_available >= 4 + attribute + for attribute in self.attributes + if attribute.handle >= request.starting_handle + and attribute.handle <= request.ending_handle + and attribute.type == request.attribute_type + and attribute.read_value(connection) == request.attribute_value + and pdu_space_available >= 4 ): # TODO: check permissions @@ -440,25 +632,27 @@ class Server(EventEmitter): if attributes: handles_information_list = [] for attribute in attributes: - if attribute.type in { + if attribute.type in ( GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, - GATT_CHARACTERISTIC_ATTRIBUTE_TYPE - }: + GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, + ): # Part of a group group_end_handle = attribute.end_group_handle else: # Not part of a group group_end_handle = attribute.handle - handles_information_list.append(struct.pack('<HH', attribute.handle, group_end_handle)) + handles_information_list.append( + struct.pack('<HH', attribute.handle, group_end_handle) + ) response = ATT_Find_By_Type_Value_Response( - handles_information_list = b''.join(handles_information_list) + handles_information_list=b''.join(handles_information_list) ) else: response = ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.starting_handle, - error_code = ATT_ATTRIBUTE_NOT_FOUND_ERROR + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.starting_handle, + error_code=ATT_ATTRIBUTE_NOT_FOUND_ERROR, ) self.send_response(connection, response) @@ -468,21 +662,39 @@ class Server(EventEmitter): See Bluetooth spec Vol 3, Part F - 3.4.4.1 Read By Type Request ''' - mtu = self.get_mtu(connection) - pdu_space_available = mtu - 2 + pdu_space_available = connection.att_mtu - 2 + + response = ATT_Error_Response( + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.starting_handle, + error_code=ATT_ATTRIBUTE_NOT_FOUND_ERROR, + ) + attributes = [] for attribute in ( - attribute for attribute in self.attributes if - attribute.type == request.attribute_type and - attribute.handle >= request.starting_handle and - attribute.handle <= request.ending_handle and - pdu_space_available + attribute + for attribute in self.attributes + if attribute.type == request.attribute_type + and attribute.handle >= request.starting_handle + and attribute.handle <= request.ending_handle + and pdu_space_available ): - # TODO: check permissions + + try: + attribute_value = attribute.read_value(connection) + except ATT_Error as error: + # If the first attribute is unreadable, return an error + # Otherwise return attributes up to this point + if not attributes: + response = ATT_Error_Response( + request_opcode_in_error=request.op_code, + attribute_handle_in_error=attribute.handle, + error_code=error.error_code, + ) + break # Check the attribute value size - attribute_value = attribute.read_value(connection) - max_attribute_size = min(mtu - 4, 253) + max_attribute_size = min(connection.att_mtu - 4, 253) if len(attribute_value) > max_attribute_size: # We need to truncate attribute_value = attribute_value[:max_attribute_size] @@ -500,17 +712,14 @@ class Server(EventEmitter): pdu_space_available -= entry_size if attributes: - attribute_data_list = [struct.pack('<H', handle) + value for handle, value in attributes] + attribute_data_list = [ + struct.pack('<H', handle) + value for handle, value in attributes + ] response = ATT_Read_By_Type_Response( - length = entry_size, - attribute_data_list = b''.join(attribute_data_list) + length=entry_size, attribute_data_list=b''.join(attribute_data_list) ) else: - response = ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.starting_handle, - error_code = ATT_ATTRIBUTE_NOT_FOUND_ERROR - ) + logging.debug(f"not found {request}") self.send_response(connection, response) @@ -520,17 +729,22 @@ class Server(EventEmitter): ''' if attribute := self.get_attribute(request.attribute_handle): - # TODO: check permissions - value = attribute.read_value(connection) - value_size = min(self.get_mtu(connection) - 1, len(value)) - response = ATT_Read_Response( - attribute_value = value[:value_size] - ) + try: + value = attribute.read_value(connection) + except ATT_Error as error: + response = ATT_Error_Response( + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.attribute_handle, + error_code=error.error_code, + ) + else: + value_size = min(connection.att_mtu - 1, len(value)) + response = ATT_Read_Response(attribute_value=value[:value_size]) else: response = ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.attribute_handle, - error_code = ATT_INVALID_HANDLE_ERROR + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.attribute_handle, + error_code=ATT_INVALID_HANDLE_ERROR, ) self.send_response(connection, response) @@ -540,31 +754,41 @@ class Server(EventEmitter): ''' if attribute := self.get_attribute(request.attribute_handle): - # TODO: check permissions - mtu = self.get_mtu(connection) - value = attribute.read_value(connection) - if request.value_offset > len(value): - response = ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.attribute_handle, - error_code = ATT_INVALID_OFFSET_ERROR - ) - elif len(value) <= mtu - 1: + try: + value = attribute.read_value(connection) + except ATT_Error as error: response = ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.attribute_handle, - error_code = ATT_ATTRIBUTE_NOT_LONG_ERROR + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.attribute_handle, + error_code=error.error_code, ) else: - part_size = min(mtu - 1, len(value) - request.value_offset) - response = ATT_Read_Blob_Response( - part_attribute_value = value[request.value_offset:request.value_offset + part_size] - ) + if request.value_offset > len(value): + response = ATT_Error_Response( + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.attribute_handle, + error_code=ATT_INVALID_OFFSET_ERROR, + ) + elif len(value) <= connection.att_mtu - 1: + response = ATT_Error_Response( + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.attribute_handle, + error_code=ATT_ATTRIBUTE_NOT_LONG_ERROR, + ) + else: + part_size = min( + connection.att_mtu - 1, len(value) - request.value_offset + ) + response = ATT_Read_Blob_Response( + part_attribute_value=value[ + request.value_offset : request.value_offset + part_size + ] + ) else: response = ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.attribute_handle, - error_code = ATT_INVALID_HANDLE_ERROR + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.attribute_handle, + error_code=ATT_INVALID_HANDLE_ERROR, ) self.send_response(connection, response) @@ -572,32 +796,33 @@ class Server(EventEmitter): ''' See Bluetooth spec Vol 3, Part F - 3.4.4.9 Read by Group Type Request ''' - if request.attribute_group_type not in { + if request.attribute_group_type not in ( GATT_PRIMARY_SERVICE_ATTRIBUTE_TYPE, GATT_SECONDARY_SERVICE_ATTRIBUTE_TYPE, - GATT_INCLUDE_ATTRIBUTE_TYPE - }: + ): response = ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.starting_handle, - error_code = ATT_UNSUPPORTED_GROUP_TYPE_ERROR + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.starting_handle, + error_code=ATT_UNSUPPORTED_GROUP_TYPE_ERROR, ) self.send_response(connection, response) return - mtu = self.get_mtu(connection) - pdu_space_available = mtu - 2 + pdu_space_available = connection.att_mtu - 2 attributes = [] for attribute in ( - attribute for attribute in self.attributes if - attribute.type == request.attribute_group_type and - attribute.handle >= request.starting_handle and - attribute.handle <= request.ending_handle and - pdu_space_available + attribute + for attribute in self.attributes + if attribute.type == request.attribute_group_type + and attribute.handle >= request.starting_handle + and attribute.handle <= request.ending_handle + and pdu_space_available ): - # Check the attribute value size + # No need to catch permission errors here, since these attributes + # must all be world-readable attribute_value = attribute.read_value(connection) - max_attribute_size = min(mtu - 6, 251) + # Check the attribute value size + max_attribute_size = min(connection.att_mtu - 6, 251) if len(attribute_value) > max_attribute_size: # We need to truncate attribute_value = attribute_value[:max_attribute_size] @@ -611,7 +836,9 @@ class Server(EventEmitter): break # Add the attribute to the list - attributes.append((attribute.handle, attribute.end_group_handle, attribute_value)) + attributes.append( + (attribute.handle, attribute.end_group_handle, attribute_value) + ) pdu_space_available -= entry_size if attributes: @@ -620,14 +847,14 @@ class Server(EventEmitter): for handle, end_group_handle, value in attributes ] response = ATT_Read_By_Group_Type_Response( - length = len(attribute_data_list[0]), - attribute_data_list = b''.join(attribute_data_list) + length=len(attribute_data_list[0]), + attribute_data_list=b''.join(attribute_data_list), ) else: response = ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.starting_handle, - error_code = ATT_ATTRIBUTE_NOT_FOUND_ERROR + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.starting_handle, + error_code=ATT_ATTRIBUTE_NOT_FOUND_ERROR, ) self.send_response(connection, response) @@ -640,22 +867,28 @@ class Server(EventEmitter): # Check that the attribute exists attribute = self.get_attribute(request.attribute_handle) if attribute is None: - self.send_response(connection, ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.attribute_handle, - error_code = ATT_INVALID_HANDLE_ERROR - )) + self.send_response( + connection, + ATT_Error_Response( + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.attribute_handle, + error_code=ATT_INVALID_HANDLE_ERROR, + ), + ) return # TODO: check permissions # Check the request parameters if len(request.attribute_value) > GATT_MAX_ATTRIBUTE_VALUE_SIZE: - self.send_response(connection, ATT_Error_Response( - request_opcode_in_error = request.op_code, - attribute_handle_in_error = request.attribute_handle, - error_code = ATT_INVALID_ATTRIBUTE_LENGTH_ERROR - )) + self.send_response( + connection, + ATT_Error_Response( + request_opcode_in_error=request.op_code, + attribute_handle_in_error=request.attribute_handle, + error_code=ATT_INVALID_ATTRIBUTE_LENGTH_ERROR, + ), + ) return # Accept the value @@ -686,13 +919,15 @@ class Server(EventEmitter): except Exception as error: logger.warning(f'!!! ignoring exception: {error}') - def on_att_handle_value_confirmation(self, connection, confirmation): + def on_att_handle_value_confirmation(self, connection, _confirmation): ''' See Bluetooth spec Vol 3, Part F - 3.4.7.3 Handle Value Confirmation ''' if self.pending_confirmations[connection.handle] is None: # Not expected! - logger.warning('!!! unexpected confirmation, there is no pending indication') + logger.warning( + '!!! unexpected confirmation, there is no pending indication' + ) return self.pending_confirmations[connection.handle].set_result(None) diff --git a/bumble/hci.py b/bumble/hci.py index 8358e30..9b5793d 100644 --- a/bumble/hci.py +++ b/bumble/hci.py @@ -15,13 +15,24 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import struct import collections import logging import functools -from colors import color +from typing import Dict, Type, Union + +from .colors import color +from .core import ( + BT_BR_EDR_TRANSPORT, + AdvertisingData, + DeviceClass, + ProtocolError, + bit_flags_to_strings, + name_or_number, + padded_bytes, +) -from .core import * # ----------------------------------------------------------------------------- # Logging @@ -33,7 +44,7 @@ logger = logging.getLogger(__name__) # Utils # ----------------------------------------------------------------------------- def hci_command_op_code(ogf, ocf): - return (ogf << 10 | ocf) + return ogf << 10 | ocf def key_with_value(dictionary, target_value): @@ -43,8 +54,8 @@ def key_with_value(dictionary, target_value): return None -def indent_lines(str): - return '\n'.join([' ' + line for line in str.split('\n')]) +def indent_lines(string): + return '\n'.join([' ' + line for line in string.split('\n')]) def map_null_terminated_utf8_string(utf8_bytes): @@ -58,13 +69,37 @@ def map_null_terminated_utf8_string(utf8_bytes): def map_class_of_device(class_of_device): - service_classes, major_device_class, minor_device_class = DeviceClass.split_class_of_device(class_of_device) - return f'[{class_of_device:06X}] Services({",".join(DeviceClass.service_class_labels(service_classes))}),Class({DeviceClass.major_device_class_name(major_device_class)}|{DeviceClass.minor_device_class_name(major_device_class, minor_device_class)})' + ( + service_classes, + major_device_class, + minor_device_class, + ) = DeviceClass.split_class_of_device(class_of_device) + return ( + f'[{class_of_device:06X}] Services(' + f'{",".join(DeviceClass.service_class_labels(service_classes))}),' + f'Class({DeviceClass.major_device_class_name(major_device_class)}|' + f'{DeviceClass.minor_device_class_name(major_device_class, minor_device_class)}' + ')' + ) + + +def phy_list_to_bits(phys): + if phys is None: + return 0 + + phy_bits = 0 + for phy in phys: + if phy not in HCI_LE_PHY_TYPE_TO_BIT: + raise ValueError('invalid PHY') + phy_bits |= 1 << HCI_LE_PHY_TYPE_TO_BIT[phy] + return phy_bits # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off +# pylint: disable=line-too-long # HCI Version HCI_VERSION_BLUETOOTH_CORE_1_0B = 0 @@ -81,6 +116,25 @@ HCI_VERSION_BLUETOOTH_CORE_5_1 = 10 HCI_VERSION_BLUETOOTH_CORE_5_2 = 11 HCI_VERSION_BLUETOOTH_CORE_5_3 = 12 +HCI_VERSION_NAMES = { + HCI_VERSION_BLUETOOTH_CORE_1_0B: 'HCI_VERSION_BLUETOOTH_CORE_1_0B', + HCI_VERSION_BLUETOOTH_CORE_1_1: 'HCI_VERSION_BLUETOOTH_CORE_1_1', + HCI_VERSION_BLUETOOTH_CORE_1_2: 'HCI_VERSION_BLUETOOTH_CORE_1_2', + HCI_VERSION_BLUETOOTH_CORE_2_0_EDR: 'HCI_VERSION_BLUETOOTH_CORE_2_0_EDR', + HCI_VERSION_BLUETOOTH_CORE_2_1_EDR: 'HCI_VERSION_BLUETOOTH_CORE_2_1_EDR', + HCI_VERSION_BLUETOOTH_CORE_3_0_HS: 'HCI_VERSION_BLUETOOTH_CORE_3_0_HS', + HCI_VERSION_BLUETOOTH_CORE_4_0: 'HCI_VERSION_BLUETOOTH_CORE_4_0', + HCI_VERSION_BLUETOOTH_CORE_4_1: 'HCI_VERSION_BLUETOOTH_CORE_4_1', + HCI_VERSION_BLUETOOTH_CORE_4_2: 'HCI_VERSION_BLUETOOTH_CORE_4_2', + HCI_VERSION_BLUETOOTH_CORE_5_0: 'HCI_VERSION_BLUETOOTH_CORE_5_0', + HCI_VERSION_BLUETOOTH_CORE_5_1: 'HCI_VERSION_BLUETOOTH_CORE_5_1', + HCI_VERSION_BLUETOOTH_CORE_5_2: 'HCI_VERSION_BLUETOOTH_CORE_5_2', + HCI_VERSION_BLUETOOTH_CORE_5_3: 'HCI_VERSION_BLUETOOTH_CORE_5_3' +} + +# LMP Version +LMP_VERSION_NAMES = HCI_VERSION_NAMES + # HCI Packet types HCI_COMMAND_PACKET = 0x01 HCI_ACL_DATA_PACKET = 0x02 @@ -88,544 +142,546 @@ HCI_SYNCHRONOUS_DATA_PACKET = 0x03 HCI_EVENT_PACKET = 0x04 # HCI Event Codes -HCI_INQUIRY_COMPLETE_EVENT = 0x01 -HCI_INQUIRY_RESULT_EVENT = 0x02 -HCI_CONNECTION_COMPLETE_EVENT = 0x03 -HCI_CONNECTION_REQUEST_EVENT = 0x04 -HCI_DISCONNECTION_COMPLETE_EVENT = 0x05 -HCI_AUTHENTICATION_COMPLETE_EVENT = 0x06 -HCI_REMOTE_NAME_REQUEST_COMPLETE_EVENT = 0x07 -HCI_ENCRYPTION_CHANGE_EVENT = 0x08 -HCI_CHANGE_CONNECTION_LINK_KEY_COMPLETE_EVENT = 0x09 -HCI_LINK_KEY_TYPE_CHANGED_EVENT = 0x0A -HCI_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE_EVENT = 0x0B -HCI_READ_REMOTE_VERSION_INFORMATION_COMPLETE_EVENT = 0x0C -HCI_QOS_SETUP_COMPLETE_EVENT = 0x0D -HCI_COMMAND_COMPLETE_EVENT = 0x0E -HCI_COMMAND_STATUS_EVENT = 0x0F -HCI_HARDWARE_ERROR_EVENT = 0x10 -HCI_FLUSH_OCCURRED_EVENT = 0x11 -HCI_ROLE_CHANGE_EVENT = 0x12 -HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT = 0x13 -HCI_MODE_CHANGE_EVENT = 0x14 -HCI_RETURN_LINK_KEYS_EVENT = 0x15 -HCI_PIN_CODE_REQUEST_EVENT = 0x16 -HCI_LINK_KEY_REQUEST_EVENT = 0x17 -HCI_LINK_KEY_NOTIFICATION_EVENT = 0x18 -HCI_LOOPBACK_COMMAND_EVENT = 0x19 -HCI_DATA_BUFFER_OVERFLOW_EVENT = 0x1A -HCI_MAX_SLOTS_CHANGE_EVENT = 0x1B -HCI_READ_CLOCK_OFFSET_COMPLETE_EVENT = 0x1C -HCI_CONNECTION_PACKET_TYPE_CHANGED_EVENT = 0x1D -HCI_QOS_VIOLATION_EVENT = 0x1E -HCI_PAGE_SCAN_REPETITION_MODE_CHANGE_EVENT = 0x20 -HCI_FLOW_SPECIFICATION_COMPLETE_EVENT = 0x21 -HCI_INQUIRY_RESULT_WITH_RSSI_EVENT = 0x22 -HCI_READ_REMOTE_EXTENDED_FEATURES_COMPLETE_EVENT = 0x23 -HCI_SYNCHRONOUS_CONNECTION_COMPLETE_EVENT = 0x2C -HCI_SYNCHRONOUS_CONNECTION_CHANGED_EVENT = 0x2D -HCI_SNIFF_SUBRATING_EVENT = 0x2E -HCI_EXTENDED_INQUIRY_RESULT_EVENT = 0x2F -HCI_ENCRYPTION_KEY_REFRESH_COMPLETE_EVENT = 0x30 -HCI_IO_CAPABILITY_REQUEST_EVENT = 0x31 -HCI_IO_CAPABILITY_RESPONSE_EVENT = 0x32 -HCI_USER_CONFIRMATION_REQUEST_EVENT = 0x33 -HCI_USER_PASSKEY_REQUEST_EVENT = 0x34 -HCI_REMOTE_OOB_DATA_REQUEST = 0x35 -HCI_SIMPLE_PAIRING_COMPLETE_EVENT = 0x36 -HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT = 0x38 -HCI_ENHANCED_FLUSH_COMPLETE_EVENT = 0x39 -HCI_USER_PASSKEY_NOTIFICATION_EVENT = 0x3B -HCI_KEYPRESS_NOTIFICATION_EVENT = 0x3C -HCI_REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION_EVENT = 0x3D -HCI_LE_META_EVENT = 0x3E -HCI_NUMBER_OF_COMPLETED_DATA_BLOCKS_EVENT = 0x48 +HCI_INQUIRY_COMPLETE_EVENT = 0x01 +HCI_INQUIRY_RESULT_EVENT = 0x02 +HCI_CONNECTION_COMPLETE_EVENT = 0x03 +HCI_CONNECTION_REQUEST_EVENT = 0x04 +HCI_DISCONNECTION_COMPLETE_EVENT = 0x05 +HCI_AUTHENTICATION_COMPLETE_EVENT = 0x06 +HCI_REMOTE_NAME_REQUEST_COMPLETE_EVENT = 0x07 +HCI_ENCRYPTION_CHANGE_EVENT = 0x08 +HCI_CHANGE_CONNECTION_LINK_KEY_COMPLETE_EVENT = 0x09 +HCI_LINK_KEY_TYPE_CHANGED_EVENT = 0x0A +HCI_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE_EVENT = 0x0B +HCI_READ_REMOTE_VERSION_INFORMATION_COMPLETE_EVENT = 0x0C +HCI_QOS_SETUP_COMPLETE_EVENT = 0x0D +HCI_COMMAND_COMPLETE_EVENT = 0x0E +HCI_COMMAND_STATUS_EVENT = 0x0F +HCI_HARDWARE_ERROR_EVENT = 0x10 +HCI_FLUSH_OCCURRED_EVENT = 0x11 +HCI_ROLE_CHANGE_EVENT = 0x12 +HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT = 0x13 +HCI_MODE_CHANGE_EVENT = 0x14 +HCI_RETURN_LINK_KEYS_EVENT = 0x15 +HCI_PIN_CODE_REQUEST_EVENT = 0x16 +HCI_LINK_KEY_REQUEST_EVENT = 0x17 +HCI_LINK_KEY_NOTIFICATION_EVENT = 0x18 +HCI_LOOPBACK_COMMAND_EVENT = 0x19 +HCI_DATA_BUFFER_OVERFLOW_EVENT = 0x1A +HCI_MAX_SLOTS_CHANGE_EVENT = 0x1B +HCI_READ_CLOCK_OFFSET_COMPLETE_EVENT = 0x1C +HCI_CONNECTION_PACKET_TYPE_CHANGED_EVENT = 0x1D +HCI_QOS_VIOLATION_EVENT = 0x1E +HCI_PAGE_SCAN_REPETITION_MODE_CHANGE_EVENT = 0x20 +HCI_FLOW_SPECIFICATION_COMPLETE_EVENT = 0x21 +HCI_INQUIRY_RESULT_WITH_RSSI_EVENT = 0x22 +HCI_READ_REMOTE_EXTENDED_FEATURES_COMPLETE_EVENT = 0x23 +HCI_SYNCHRONOUS_CONNECTION_COMPLETE_EVENT = 0x2C +HCI_SYNCHRONOUS_CONNECTION_CHANGED_EVENT = 0x2D +HCI_SNIFF_SUBRATING_EVENT = 0x2E +HCI_EXTENDED_INQUIRY_RESULT_EVENT = 0x2F +HCI_ENCRYPTION_KEY_REFRESH_COMPLETE_EVENT = 0x30 +HCI_IO_CAPABILITY_REQUEST_EVENT = 0x31 +HCI_IO_CAPABILITY_RESPONSE_EVENT = 0x32 +HCI_USER_CONFIRMATION_REQUEST_EVENT = 0x33 +HCI_USER_PASSKEY_REQUEST_EVENT = 0x34 +HCI_REMOTE_OOB_DATA_REQUEST = 0x35 +HCI_SIMPLE_PAIRING_COMPLETE_EVENT = 0x36 +HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT = 0x38 +HCI_ENHANCED_FLUSH_COMPLETE_EVENT = 0x39 +HCI_USER_PASSKEY_NOTIFICATION_EVENT = 0x3B +HCI_KEYPRESS_NOTIFICATION_EVENT = 0x3C +HCI_REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION_EVENT = 0x3D +HCI_LE_META_EVENT = 0x3E +HCI_NUMBER_OF_COMPLETED_DATA_BLOCKS_EVENT = 0x48 +HCI_TRIGGERED_CLOCK_CAPTURE_EVENT = 0X4E +HCI_SYNCHRONIZATION_TRAIN_COMPLETE_EVENT = 0X4F +HCI_SYNCHRONIZATION_TRAIN_RECEIVED_EVENT = 0X50 +HCI_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE_EVENT = 0X51 +HCI_CONNECTIONLESS_PERIPHERAL_BROADCAST_TIMEOUT_EVENT = 0X52 +HCI_TRUNCATED_PAGE_COMPLETE_EVENT = 0X53 +HCI_PERIPHERAL_PAGE_RESPONSE_TIMEOUT_EVENT = 0X54 +HCI_CONNECTIONLESS_PERIPHERAL_BROADCAST_CHANNEL_MAP_CHANGE_EVENT = 0X55 +HCI_INQUIRY_RESPONSE_NOTIFICATION_EVENT = 0X56 +HCI_AUTHENTICATED_PAYLOAD_TIMEOUT_EXPIRED_EVENT = 0X57 +HCI_SAM_STATUS_CHANGE_EVENT = 0X58 HCI_EVENT_NAMES = { - HCI_INQUIRY_COMPLETE_EVENT: 'HCI_INQUIRY_COMPLETE_EVENT', - HCI_INQUIRY_RESULT_EVENT: 'HCI_INQUIRY_RESULT_EVENT', - HCI_CONNECTION_COMPLETE_EVENT: 'HCI_CONNECTION_COMPLETE_EVENT', - HCI_CONNECTION_REQUEST_EVENT: 'HCI_CONNECTION_REQUEST_EVENT', - HCI_DISCONNECTION_COMPLETE_EVENT: 'HCI_DISCONNECTION_COMPLETE_EVENT', - HCI_AUTHENTICATION_COMPLETE_EVENT: 'HCI_AUTHENTICATION_COMPLETE_EVENT', - HCI_REMOTE_NAME_REQUEST_COMPLETE_EVENT: 'HCI_REMOTE_NAME_REQUEST_COMPLETE_EVENT', - HCI_ENCRYPTION_CHANGE_EVENT: 'HCI_ENCRYPTION_CHANGE_EVENT', - HCI_CHANGE_CONNECTION_LINK_KEY_COMPLETE_EVENT: 'HCI_CHANGE_CONNECTION_LINK_KEY_COMPLETE_EVENT', - HCI_LINK_KEY_TYPE_CHANGED_EVENT: 'HCI_LINK_KEY_TYPE_CHANGED_EVENT', - HCI_INQUIRY_RESULT_WITH_RSSI_EVENT: 'HCI_INQUIRY_RESULT_WITH_RSSI_EVENT', - HCI_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE_EVENT: 'HCI_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE_EVENT', - HCI_READ_REMOTE_VERSION_INFORMATION_COMPLETE_EVENT: 'HCI_READ_REMOTE_VERSION_INFORMATION_COMPLETE_EVENT', - HCI_QOS_SETUP_COMPLETE_EVENT: 'HCI_QOS_SETUP_COMPLETE_EVENT', - HCI_SYNCHRONOUS_CONNECTION_COMPLETE_EVENT: 'HCI_SYNCHRONOUS_CONNECTION_COMPLETE_EVENT', - HCI_SYNCHRONOUS_CONNECTION_CHANGED_EVENT: 'HCI_SYNCHRONOUS_CONNECTION_CHANGED_EVENT', - HCI_SNIFF_SUBRATING_EVENT: 'HCI_SNIFF_SUBRATING_EVENT', - HCI_COMMAND_COMPLETE_EVENT: 'HCI_COMMAND_COMPLETE_EVENT', - HCI_COMMAND_STATUS_EVENT: 'HCI_COMMAND_STATUS_EVENT', - HCI_HARDWARE_ERROR_EVENT: 'HCI_HARDWARE_ERROR_EVENT', - HCI_FLUSH_OCCURRED_EVENT: 'HCI_FLUSH_OCCURRED_EVENT', - HCI_ROLE_CHANGE_EVENT: 'HCI_ROLE_CHANGE_EVENT', - HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT: 'HCI_NUMBER_OF_COMPLETED_PACKETS_EVENT', - HCI_MODE_CHANGE_EVENT: 'HCI_MODE_CHANGE_EVENT', - HCI_RETURN_LINK_KEYS_EVENT: 'HCI_RETURN_LINK_KEYS_EVENT', - HCI_PIN_CODE_REQUEST_EVENT: 'HCI_PIN_CODE_REQUEST_EVENT', - HCI_LINK_KEY_REQUEST_EVENT: 'HCI_LINK_KEY_REQUEST_EVENT', - HCI_LINK_KEY_NOTIFICATION_EVENT: 'HCI_LINK_KEY_NOTIFICATION_EVENT', - HCI_LOOPBACK_COMMAND_EVENT: 'HCI_LOOPBACK_COMMAND_EVENT', - HCI_DATA_BUFFER_OVERFLOW_EVENT: 'HCI_DATA_BUFFER_OVERFLOW_EVENT', - HCI_MAX_SLOTS_CHANGE_EVENT: 'HCI_MAX_SLOTS_CHANGE_EVENT', - HCI_READ_CLOCK_OFFSET_COMPLETE_EVENT: 'HCI_READ_CLOCK_OFFSET_COMPLETE_EVENT', - HCI_CONNECTION_PACKET_TYPE_CHANGED_EVENT: 'HCI_CONNECTION_PACKET_TYPE_CHANGED_EVENT', - HCI_QOS_VIOLATION_EVENT: 'HCI_QOS_VIOLATION_EVENT', - HCI_PAGE_SCAN_REPETITION_MODE_CHANGE_EVENT: 'HCI_PAGE_SCAN_REPETITION_MODE_CHANGE_EVENT', - HCI_FLOW_SPECIFICATION_COMPLETE_EVENT: 'HCI_FLOW_SPECIFICATION_COMPLETE_EVENT', - HCI_READ_REMOTE_EXTENDED_FEATURES_COMPLETE_EVENT: 'HCI_READ_REMOTE_EXTENDED_FEATURES_COMPLETE_EVENT', - HCI_EXTENDED_INQUIRY_RESULT_EVENT: 'HCI_EXTENDED_INQUIRY_RESULT_EVENT', - HCI_ENCRYPTION_KEY_REFRESH_COMPLETE_EVENT: 'HCI_ENCRYPTION_KEY_REFRESH_COMPLETE_EVENT', - HCI_IO_CAPABILITY_REQUEST_EVENT: 'HCI_IO_CAPABILITY_REQUEST_EVENT', - HCI_IO_CAPABILITY_RESPONSE_EVENT: 'HCI_IO_CAPABILITY_RESPONSE_EVENT', - HCI_USER_CONFIRMATION_REQUEST_EVENT: 'HCI_USER_CONFIRMATION_REQUEST_EVENT', - HCI_USER_PASSKEY_REQUEST_EVENT: 'HCI_USER_PASSKEY_REQUEST_EVENT', - HCI_REMOTE_OOB_DATA_REQUEST: 'HCI_REMOTE_OOB_DATA_REQUEST', - HCI_SIMPLE_PAIRING_COMPLETE_EVENT: 'HCI_SIMPLE_PAIRING_COMPLETE_EVENT', - HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT: 'HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT', - HCI_ENHANCED_FLUSH_COMPLETE_EVENT: 'HCI_ENHANCED_FLUSH_COMPLETE_EVENT', - HCI_USER_PASSKEY_NOTIFICATION_EVENT: 'HCI_USER_PASSKEY_NOTIFICATION_EVENT', - HCI_KEYPRESS_NOTIFICATION_EVENT: 'HCI_KEYPRESS_NOTIFICATION_EVENT', - HCI_REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION_EVENT: 'HCI_REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION_EVENT', - HCI_LE_META_EVENT: 'HCI_LE_META_EVENT' + event_code: event_name for (event_name, event_code) in globals().items() + if event_name.startswith('HCI_') and event_name.endswith('_EVENT') } # HCI Subevent Codes -HCI_LE_CONNECTION_COMPLETE_EVENT = 0x01 -HCI_LE_ADVERTISING_REPORT_EVENT = 0x02 -HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT = 0x03 -HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT = 0x04 -HCI_LE_LONG_TERM_KEY_REQUEST_EVENT = 0x05 -HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT = 0x06 -HCI_LE_DATA_LENGTH_CHANGE_EVENT = 0x07 -HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT = 0x08 -HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT = 0x09 -HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT = 0x0A -HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT = 0x0B -HCI_LE_PHY_UPDATE_COMPLETE_EVENT = 0x0C -HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT = 0x0D -HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT = 0x0E -HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT = 0x0F -HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT = 0x10 -HCI_LE_SCAN_TIMEOUT_EVENT = 0x11 -HCI_LE_ADVERTISING_SET_TERMINATED_EVENT = 0x12 -HCI_LE_SCAN_REQUEST_RECEIVED_EVENT = 0x13 -HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT = 0x14 +HCI_LE_CONNECTION_COMPLETE_EVENT = 0x01 +HCI_LE_ADVERTISING_REPORT_EVENT = 0x02 +HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT = 0x03 +HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT = 0x04 +HCI_LE_LONG_TERM_KEY_REQUEST_EVENT = 0x05 +HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT = 0x06 +HCI_LE_DATA_LENGTH_CHANGE_EVENT = 0x07 +HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT = 0x08 +HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT = 0x09 +HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT = 0x0A +HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT = 0x0B +HCI_LE_PHY_UPDATE_COMPLETE_EVENT = 0x0C +HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT = 0x0D +HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT = 0x0E +HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT = 0x0F +HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT = 0x10 +HCI_LE_SCAN_TIMEOUT_EVENT = 0x11 +HCI_LE_ADVERTISING_SET_TERMINATED_EVENT = 0x12 +HCI_LE_SCAN_REQUEST_RECEIVED_EVENT = 0x13 +HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT = 0x14 +HCI_LE_CONNECTIONLESS_IQ_REPORT_EVENT = 0X15 +HCI_LE_CONNECTION_IQ_REPORT_EVENT = 0X16 +HCI_LE_CTE_REQUEST_FAILED_EVENT = 0X17 +HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED_EVENT = 0X18 +HCI_LE_CIS_ESTABLISHED_EVENT = 0X19 +HCI_LE_CIS_REQUEST_EVENT = 0X1A +HCI_LE_CREATE_BIG_COMPLETE_EVENT = 0X1B +HCI_LE_TERMINATE_BIG_COMPLETE_EVENT = 0X1C +HCI_LE_BIG_SYNC_ESTABLISHED_EVENT = 0X1D +HCI_LE_BIG_SYNC_LOST_EVENT = 0X1E +HCI_LE_REQUEST_PEER_SCA_COMPLETE_EVENT = 0X1F +HCI_LE_PATH_LOSS_THRESHOLD_EVENT = 0X20 +HCI_LE_TRANSMIT_POWER_REPORTING_EVENT = 0X21 +HCI_LE_BIGINFO_ADVERTISING_REPORT_EVENT = 0X22 +HCI_LE_SUBRATE_CHANGE_EVENT = 0X23 HCI_SUBEVENT_NAMES = { - HCI_LE_CONNECTION_COMPLETE_EVENT: 'HCI_LE_CONNECTION_COMPLETE_EVENT', - HCI_LE_ADVERTISING_REPORT_EVENT: 'HCI_LE_ADVERTISING_REPORT_EVENT', - HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT: 'HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT', - HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT: 'HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT', - HCI_LE_LONG_TERM_KEY_REQUEST_EVENT: 'HCI_LE_LONG_TERM_KEY_REQUEST_EVENT', - HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT: 'HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT', - HCI_LE_DATA_LENGTH_CHANGE_EVENT: 'HCI_LE_DATA_LENGTH_CHANGE_EVENT', - HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT: 'HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT', - HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT: 'HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT', - HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT: 'HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT', - HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT: 'HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT', - HCI_LE_PHY_UPDATE_COMPLETE_EVENT: 'HCI_LE_PHY_UPDATE_COMPLETE_EVENT', - HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT: 'HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT', - HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT: 'HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT', - HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT: 'HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT', - HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT: 'HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT', - HCI_LE_SCAN_TIMEOUT_EVENT: 'HCI_LE_SCAN_TIMEOUT_EVENT', - HCI_LE_ADVERTISING_SET_TERMINATED_EVENT: 'HCI_LE_ADVERTISING_SET_TERMINATED_EVENT', - HCI_LE_SCAN_REQUEST_RECEIVED_EVENT: 'HCI_LE_SCAN_REQUEST_RECEIVED_EVENT', - HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT: 'HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT' + event_code: event_name for (event_name, event_code) in globals().items() + if event_name.startswith('HCI_LE_') and event_name.endswith('_EVENT') and event_code != HCI_LE_META_EVENT } # HCI Command -HCI_INQUIRY_COMMAND = hci_command_op_code(0x01, 0x0001) -HCI_INQUIRY_CANCEL_COMMAND = hci_command_op_code(0x01, 0x0002) -HCI_CREATE_CONNECTION_COMMAND = hci_command_op_code(0x01, 0x0005) -HCI_DISCONNECT_COMMAND = hci_command_op_code(0x01, 0x0006) -HCI_ACCEPT_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x0009) -HCI_LINK_KEY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x000B) -HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x000C) -HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x000E) -HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND = hci_command_op_code(0x01, 0x000F) -HCI_AUTHENTICATION_REQUESTED_COMMAND = hci_command_op_code(0x01, 0x0011) -HCI_SET_CONNECTION_ENCRYPTION_COMMAND = hci_command_op_code(0x01, 0x0013) -HCI_REMOTE_NAME_REQUEST_COMMAND = hci_command_op_code(0x01, 0x0019) -HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x01, 0x001B) -HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND = hci_command_op_code(0x01, 0x001C) -HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND = hci_command_op_code(0x01, 0x001D) -HCI_READ_CLOCK_OFFSET_COMMAND = hci_command_op_code(0x01, 0x001F) -HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x002B) -HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x002C) -HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x002D) -HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x002E) -HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x002F) -HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND = hci_command_op_code(0x01, 0x003D) -HCI_SNIFF_MODE_COMMAND = hci_command_op_code(0x02, 0x0003) -HCI_EXIT_SNIFF_MODE_COMMAND = hci_command_op_code(0x02, 0x0004) -HCI_SWITCH_ROLE_COMMAND = hci_command_op_code(0x02, 0x000B) -HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000D) -HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000F) -HCI_SNIFF_SUBRATING_COMMAND = hci_command_op_code(0x02, 0x0011) -HCI_SET_EVENT_MASK_COMMAND = hci_command_op_code(0x03, 0x0001) -HCI_RESET_COMMAND = hci_command_op_code(0x03, 0x0003) -HCI_SET_EVENT_FILTER_COMMAND = hci_command_op_code(0x03, 0x0005) -HCI_READ_STORED_LINK_KEY_COMMAND = hci_command_op_code(0x03, 0x000D) -HCI_DELETE_STORED_LINK_KEY_COMMAND = hci_command_op_code(0x03, 0x0012) -HCI_WRITE_LOCAL_NAME_COMMAND = hci_command_op_code(0x03, 0x0013) -HCI_READ_LOCAL_NAME_COMMAND = hci_command_op_code(0x03, 0x0014) -HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0016) -HCI_WRITE_PAGE_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0018) -HCI_WRITE_SCAN_ENABLE_COMMAND = hci_command_op_code(0x03, 0x001A) -HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001B) -HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001C) -HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001E) -HCI_READ_CLASS_OF_DEVICE_COMMAND = hci_command_op_code(0x03, 0x0023) -HCI_WRITE_CLASS_OF_DEVICE_COMMAND = hci_command_op_code(0x03, 0x0024) -HCI_READ_VOICE_SETTING_COMMAND = hci_command_op_code(0x03, 0x0025) -HCI_WRITE_VOICE_SETTING_COMMAND = hci_command_op_code(0x03, 0x0026) -HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND = hci_command_op_code(0x03, 0x002E) -HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND = hci_command_op_code(0x03, 0x002F) -HCI_HOST_BUFFER_SIZE_COMMAND = hci_command_op_code(0x03, 0x0033) -HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0037) -HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND = hci_command_op_code(0x03, 0x0038) -HCI_READ_CURRENT_IAC_LAP_COMMAND = hci_command_op_code(0x03, 0x0039) -HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0043) -HCI_WRITE_INQUIRY_MODE_COMMAND = hci_command_op_code(0x03, 0x0045) -HCI_READ_PAGE_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0046) -HCI_WRITE_PAGE_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0047) -HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND = hci_command_op_code(0x03, 0x0052) -HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND = hci_command_op_code(0x03, 0x0056) -HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x03, 0x0058) -HCI_SET_EVENT_MASK_PAGE_2_COMMAND = hci_command_op_code(0x03, 0x0063) -HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND = hci_command_op_code(0x03, 0x005A) -HCI_READ_LE_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x006C) -HCI_WRITE_LE_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x006D) -HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x007A) -HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x007C) -HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND = hci_command_op_code(0x04, 0x0001) -HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND = hci_command_op_code(0x04, 0x0002) -HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x04, 0x0003) -HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND = hci_command_op_code(0x04, 0x0004) -HCI_READ_BUFFER_SIZE_COMMAND = hci_command_op_code(0x04, 0x0005) -HCI_READ_BD_ADDR_COMMAND = hci_command_op_code(0x04, 0x0009) -HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND = hci_command_op_code(0x04, 0x000B) -HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND = hci_command_op_code(0x05, 0x0008) -HCI_LE_SET_EVENT_MASK_COMMAND = hci_command_op_code(0x08, 0x0001) -HCI_LE_READ_BUFFER_SIZE_COMMAND = hci_command_op_code(0x08, 0x0002) -HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0003) -HCI_LE_SET_RANDOM_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x0005) -HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0006) -HCI_LE_READ_ADVERTISING_CHANNEL_TX_POWER_COMMAND = hci_command_op_code(0x08, 0x0007) -HCI_LE_SET_ADVERTISING_DATA_COMMAND = hci_command_op_code(0x08, 0x0008) -HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0009) -HCI_LE_SET_ADVERTISING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x000A) -HCI_LE_SET_SCAN_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x000B) -HCI_LE_SET_SCAN_ENABLE_COMMAND = hci_command_op_code(0x08, 0x000C) -HCI_LE_CREATE_CONNECTION_COMMAND = hci_command_op_code(0x08, 0x000D) -HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND = hci_command_op_code(0x08, 0x000E) -HCI_LE_READ_WHITE_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x000F) -HCI_LE_CLEAR_WHITE_LIST_COMMAND = hci_command_op_code(0x08, 0x0010) -HCI_LE_ADD_DEVICE_TO_WHITE_LIST_COMMAND = hci_command_op_code(0x08, 0x0011) -HCI_LE_REMOVE_DEVICE_FROM_WHITE_LIST_COMMAND = hci_command_op_code(0x08, 0x0012) -HCI_LE_CONNECTION_UPDATE_COMMAND = hci_command_op_code(0x08, 0x0013) -HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND = hci_command_op_code(0x08, 0x0014) -HCI_LE_READ_CHANNEL_MAP_COMMAND = hci_command_op_code(0x08, 0x0015) -HCI_LE_READ_REMOTE_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0016) -HCI_LE_ENCRYPT_COMMAND = hci_command_op_code(0x08, 0x0017) -HCI_LE_RAND_COMMAND = hci_command_op_code(0x08, 0x0018) -HCI_LE_START_ENCRYPTION_COMMAND = hci_command_op_code(0x08, 0x0019) -HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x08, 0x001A) -HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x08, 0x001B) -HCI_LE_READ_SUPPORTED_STATES_COMMAND = hci_command_op_code(0x08, 0x001C) -HCI_LE_RECEIVER_TEST_COMMAND = hci_command_op_code(0x08, 0x001D) -HCI_LE_TRANSMITTER_TEST_COMMAND = hci_command_op_code(0x08, 0x001E) -HCI_LE_TEST_END_COMMAND = hci_command_op_code(0x08, 0x001F) -HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND = hci_command_op_code(0x08, 0x0020) -HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x08, 0x0021) -HCI_LE_SET_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x0022) -HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x0023) -HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x0024) -HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND = hci_command_op_code(0x08, 0x0025) -HCI_LE_GENERATE_DHKEY_COMMAND = hci_command_op_code(0x08, 0x0026) -HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND = hci_command_op_code(0x08, 0x0027) -HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND = hci_command_op_code(0x08, 0x0028) -HCI_LE_CLEAR_RESOLVING_LIST_COMMAND = hci_command_op_code(0x08, 0x0029) -HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x002A) -HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x002B) -HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x002C) -HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND = hci_command_op_code(0x08, 0x002D) -HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND = hci_command_op_code(0x08, 0x002E) -HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x002F) -HCI_LE_READ_PHY_COMMAND = hci_command_op_code(0x08, 0x0030) -HCI_LE_SET_DEFAULT_PHY_COMMAND = hci_command_op_code(0x08, 0x0031) -HCI_LE_SET_PHY_COMMAND = hci_command_op_code(0x08, 0x0032) -HCI_LE_ENHANCED_RECEIVER_TEST_COMMAND = hci_command_op_code(0x08, 0x0033) -HCI_LE_ENHANCED_TRANSMITTER_TEST_COMMAND = hci_command_op_code(0x08, 0x0034) -HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x0035) -HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0036) -HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND = hci_command_op_code(0x08, 0x0037) -HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0038) -HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0039) -HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x003A) -HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERETISING_SETS_COMMAND = hci_command_op_code(0x08, 0x003B) -HCI_LE_REMOVE_ADVERTISING_SET_COMMAND = hci_command_op_code(0x08, 0x003C) -HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND = hci_command_op_code(0x08, 0x003D) -HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x003E) -HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND = hci_command_op_code(0x08, 0x003F) -HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0040) -HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0041) -HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0042) -HCI_LE_SET_EXTENDED_CREATE_CONNECTION_COMMAND = hci_command_op_code(0x08, 0x0043) -HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x0044) -HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND = hci_command_op_code(0x08, 0x0045) -HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x0046) -HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND = hci_command_op_code(0x08, 0x0047) -HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND = hci_command_op_code(0x08, 0x0048) -HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND = hci_command_op_code(0x08, 0x0049) -HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x004A) -HCI_LE_READ_TRANSMIT_POWER_COMMAND = hci_command_op_code(0x08, 0x004B) -HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND = hci_command_op_code(0x08, 0x004C) -HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND = hci_command_op_code(0x08, 0x004D) -HCI_LE_SET_PRIVACY_MODE_COMMAND = hci_command_op_code(0x08, 0x004E) - +HCI_INQUIRY_COMMAND = hci_command_op_code(0x01, 0x0001) +HCI_INQUIRY_CANCEL_COMMAND = hci_command_op_code(0x01, 0x0002) +HCI_PERIODIC_INQUIRY_MODE_COMMAND = hci_command_op_code(0x01, 0x0003) +HCI_EXIT_PERIODIC_INQUIRY_MODE_COMMAND = hci_command_op_code(0x01, 0x0004) +HCI_CREATE_CONNECTION_COMMAND = hci_command_op_code(0x01, 0x0005) +HCI_DISCONNECT_COMMAND = hci_command_op_code(0x01, 0x0006) +HCI_CREATE_CONNECTION_CANCEL_COMMAND = hci_command_op_code(0x01, 0x0008) +HCI_ACCEPT_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x0009) +HCI_REJECT_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x000A) +HCI_LINK_KEY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x000B) +HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x000C) +HCI_PIN_CODE_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x000D) +HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x000E) +HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND = hci_command_op_code(0x01, 0x000F) +HCI_AUTHENTICATION_REQUESTED_COMMAND = hci_command_op_code(0x01, 0x0011) +HCI_SET_CONNECTION_ENCRYPTION_COMMAND = hci_command_op_code(0x01, 0x0013) +HCI_CHANGE_CONNECTION_LINK_KEY_COMMAND = hci_command_op_code(0x01, 0x0015) +HCI_LINK_KEY_SELECTION_COMMAND = hci_command_op_code(0x01, 0x0017) +HCI_REMOTE_NAME_REQUEST_COMMAND = hci_command_op_code(0x01, 0x0019) +HCI_REMOTE_NAME_REQUEST_CANCEL_COMMAND = hci_command_op_code(0x01, 0x001A) +HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x01, 0x001B) +HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND = hci_command_op_code(0x01, 0x001C) +HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND = hci_command_op_code(0x01, 0x001D) +HCI_READ_CLOCK_OFFSET_COMMAND = hci_command_op_code(0x01, 0x001F) +HCI_READ_LMP_HANDLE_COMMAND = hci_command_op_code(0x01, 0x0020) +HCI_SETUP_SYNCHRONOUS_CONNECTION_COMMAND = hci_command_op_code(0x01, 0x0028) +HCI_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x0029) +HCI_REJECT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x002A) +HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x002B) +HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x002C) +HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x002D) +HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x002E) +HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x002F) +HCI_REMOTE_OOB_DATA_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x0030) +HCI_REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x0033) +HCI_IO_CAPABILITY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x01, 0x0034) +HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND = hci_command_op_code(0x01, 0x003D) +HCI_ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND = hci_command_op_code(0x01, 0x003E) +HCI_TRUNCATED_PAGE_COMMAND = hci_command_op_code(0x01, 0x003F) +HCI_TRUNCATED_PAGE_CANCEL_COMMAND = hci_command_op_code(0x01, 0x0040) +HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_COMMAND = hci_command_op_code(0x01, 0x0041) +HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE_COMMAND = hci_command_op_code(0x01, 0x0042) +HCI_START_SYNCHRONIZATION_TRAIN_COMMAND = hci_command_op_code(0x01, 0x0043) +HCI_RECEIVE_SYNCHRONIZATION_TRAIN_COMMAND = hci_command_op_code(0x01, 0x0044) +HCI_REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY_COMMAND = hci_command_op_code(0x01, 0x0045) +HCI_HOLD_MODE_COMMAND = hci_command_op_code(0x02, 0x0001) +HCI_SNIFF_MODE_COMMAND = hci_command_op_code(0x02, 0x0003) +HCI_EXIT_SNIFF_MODE_COMMAND = hci_command_op_code(0x02, 0x0004) +HCI_QOS_SETUP_COMMAND = hci_command_op_code(0x02, 0x0007) +HCI_ROLE_DISCOVERY_COMMAND = hci_command_op_code(0x02, 0x0009) +HCI_SWITCH_ROLE_COMMAND = hci_command_op_code(0x02, 0x000B) +HCI_READ_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000C) +HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000D) +HCI_READ_DEFAULT_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000E) +HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND = hci_command_op_code(0x02, 0x000F) +HCI_FLOW_SPECIFICATION_COMMAND = hci_command_op_code(0x02, 0x0010) +HCI_SNIFF_SUBRATING_COMMAND = hci_command_op_code(0x02, 0x0011) +HCI_SET_EVENT_MASK_COMMAND = hci_command_op_code(0x03, 0x0001) +HCI_RESET_COMMAND = hci_command_op_code(0x03, 0x0003) +HCI_SET_EVENT_FILTER_COMMAND = hci_command_op_code(0x03, 0x0005) +HCI_FLUSH_COMMAND = hci_command_op_code(0x03, 0x0008) +HCI_READ_PIN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0009) +HCI_WRITE_PIN_TYPE_COMMAND = hci_command_op_code(0x03, 0x000A) +HCI_READ_STORED_LINK_KEY_COMMAND = hci_command_op_code(0x03, 0x000D) +HCI_WRITE_STORED_LINK_KEY_COMMAND = hci_command_op_code(0x03, 0x0011) +HCI_DELETE_STORED_LINK_KEY_COMMAND = hci_command_op_code(0x03, 0x0012) +HCI_WRITE_LOCAL_NAME_COMMAND = hci_command_op_code(0x03, 0x0013) +HCI_READ_LOCAL_NAME_COMMAND = hci_command_op_code(0x03, 0x0014) +HCI_READ_CONNECTION_ACCEPT_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0015) +HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0016) +HCI_READ_PAGE_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0017) +HCI_WRITE_PAGE_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0018) +HCI_READ_SCAN_ENABLE_COMMAND = hci_command_op_code(0x03, 0x0019) +HCI_WRITE_SCAN_ENABLE_COMMAND = hci_command_op_code(0x03, 0x001A) +HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001B) +HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001C) +HCI_READ_INQUIRY_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001D) +HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x001E) +HCI_READ_AUTHENTICATION_ENABLE_COMMAND = hci_command_op_code(0x03, 0x001F) +HCI_WRITE_AUTHENTICATION_ENABLE_COMMAND = hci_command_op_code(0x03, 0x0020) +HCI_READ_CLASS_OF_DEVICE_COMMAND = hci_command_op_code(0x03, 0x0023) +HCI_WRITE_CLASS_OF_DEVICE_COMMAND = hci_command_op_code(0x03, 0x0024) +HCI_READ_VOICE_SETTING_COMMAND = hci_command_op_code(0x03, 0x0025) +HCI_WRITE_VOICE_SETTING_COMMAND = hci_command_op_code(0x03, 0x0026) +HCI_READ_AUTOMATIC_FLUSH_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0027) +HCI_WRITE_AUTOMATIC_FLUSH_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0028) +HCI_READ_NUM_BROADCAST_RETRANSMISSIONS_COMMAND = hci_command_op_code(0x03, 0x0029) +HCI_WRITE_NUM_BROADCAST_RETRANSMISSIONS_COMMAND = hci_command_op_code(0x03, 0x002A) +HCI_READ_HOLD_MODE_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x002B) +HCI_WRITE_HOLD_MODE_ACTIVITY_COMMAND = hci_command_op_code(0x03, 0x002C) +HCI_READ_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x03, 0x002D) +HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND = hci_command_op_code(0x03, 0x002E) +HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND = hci_command_op_code(0x03, 0x002F) +HCI_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_COMMAND = hci_command_op_code(0x03, 0x0031) +HCI_HOST_BUFFER_SIZE_COMMAND = hci_command_op_code(0x03, 0x0033) +HCI_HOST_NUMBER_OF_COMPLETED_PACKETS_COMMAND = hci_command_op_code(0x03, 0x0035) +HCI_READ_LINK_SUPERVISION_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0036) +HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x0037) +HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND = hci_command_op_code(0x03, 0x0038) +HCI_READ_CURRENT_IAC_LAP_COMMAND = hci_command_op_code(0x03, 0x0039) +HCI_WRITE_CURRENT_IAC_LAP_COMMAND = hci_command_op_code(0x03, 0x003A) +HCI_SET_AFH_HOST_CHANNEL_CLASSIFICATION_COMMAND = hci_command_op_code(0x03, 0x003F) +HCI_READ_INQUIRY_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0042) +HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0043) +HCI_READ_INQUIRY_MODE_COMMAND = hci_command_op_code(0x03, 0x0044) +HCI_WRITE_INQUIRY_MODE_COMMAND = hci_command_op_code(0x03, 0x0045) +HCI_READ_PAGE_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0046) +HCI_WRITE_PAGE_SCAN_TYPE_COMMAND = hci_command_op_code(0x03, 0x0047) +HCI_READ_AFH_CHANNEL_ASSESSMENT_MODE_COMMAND = hci_command_op_code(0x03, 0x0048) +HCI_WRITE_AFH_CHANNEL_ASSESSMENT_MODE_COMMAND = hci_command_op_code(0x03, 0x0049) +HCI_READ_EXTENDED_INQUIRY_RESPONSE_COMMAND = hci_command_op_code(0x03, 0x0051) +HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND = hci_command_op_code(0x03, 0x0052) +HCI_REFRESH_ENCRYPTION_KEY_COMMAND = hci_command_op_code(0x03, 0x0053) +HCI_READ_SIMPLE_PAIRING_MODE_COMMAND = hci_command_op_code(0x03, 0x0055) +HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND = hci_command_op_code(0x03, 0x0056) +HCI_READ_LOCAL_OOB_DATA_COMMAND = hci_command_op_code(0x03, 0x0057) +HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x03, 0x0058) +HCI_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x03, 0x0059) +HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND = hci_command_op_code(0x03, 0x005A) +HCI_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND = hci_command_op_code(0x03, 0x005B) +HCI_ENHANCED_FLUSH_COMMAND = hci_command_op_code(0x03, 0x005F) +HCI_SEND_KEYPRESS_NOTIFICATION_COMMAND = hci_command_op_code(0x03, 0x0060) +HCI_SET_EVENT_MASK_PAGE_2_COMMAND = hci_command_op_code(0x03, 0x0063) +HCI_READ_FLOW_CONTROL_MODE_COMMAND = hci_command_op_code(0x03, 0x0066) +HCI_WRITE_FLOW_CONTROL_MODE_COMMAND = hci_command_op_code(0x03, 0x0067) +HCI_READ_ENHANCED_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x03, 0x0068) +HCI_READ_LE_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x006C) +HCI_WRITE_LE_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x006D) +HCI_SET_MWS_CHANNEL_PARAMETERS_COMMAND = hci_command_op_code(0x03, 0x006E) +HCI_SET_EXTERNAL_FRAME_CONFIGURATION_COMMAND = hci_command_op_code(0x03, 0x006F) +HCI_SET_MWS_SIGNALING_COMMAND = hci_command_op_code(0x03, 0x0070) +HCI_SET_MWS_TRANSPORT_LAYER_COMMAND = hci_command_op_code(0x03, 0x0071) +HCI_SET_MWS_SCAN_FREQUENCY_TABLE_COMMAND = hci_command_op_code(0x03, 0x0072) +HCI_SET_MWS_PATTERN_CONFIGURATION_COMMAND = hci_command_op_code(0x03, 0x0073) +HCI_SET_RESERVED_LT_ADDR_COMMAND = hci_command_op_code(0x03, 0x0074) +HCI_DELETE_RESERVED_LT_ADDR_COMMAND = hci_command_op_code(0x03, 0x0075) +HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_DATA_COMMAND = hci_command_op_code(0x03, 0x0076) +HCI_READ_SYNCHRONIZATION_TRAIN_PARAMETERS_COMMAND = hci_command_op_code(0x03, 0x0077) +HCI_WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS_COMMAND = hci_command_op_code(0x03, 0x0078) +HCI_READ_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x0079) +HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND = hci_command_op_code(0x03, 0x007A) +HCI_READ_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x007B) +HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x007C) +HCI_READ_LOCAL_OOB_EXTENDED_DATA_COMMAND = hci_command_op_code(0x03, 0x007D) +HCI_READ_EXTENDED_PAGE_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x007E) +HCI_WRITE_EXTENDED_PAGE_TIMEOUT_COMMAND = hci_command_op_code(0x03, 0x007F) +HCI_READ_EXTENDED_INQUIRY_LENGTH_COMMAND = hci_command_op_code(0x03, 0x0080) +HCI_WRITE_EXTENDED_INQUIRY_LENGTH_COMMAND = hci_command_op_code(0x03, 0x0081) +HCI_SET_ECOSYSTEM_BASE_INTERVAL_COMMAND = hci_command_op_code(0x03, 0x0082) +HCI_CONFIGURE_DATA_PATH_COMMAND = hci_command_op_code(0x03, 0x0083) +HCI_SET_MIN_ENCRYPTION_KEY_SIZE_COMMAND = hci_command_op_code(0x03, 0x0084) +HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND = hci_command_op_code(0x04, 0x0001) +HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND = hci_command_op_code(0x04, 0x0002) +HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x04, 0x0003) +HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND = hci_command_op_code(0x04, 0x0004) +HCI_READ_BUFFER_SIZE_COMMAND = hci_command_op_code(0x04, 0x0005) +HCI_READ_BD_ADDR_COMMAND = hci_command_op_code(0x04, 0x0009) +HCI_READ_DATA_BLOCK_SIZE_COMMAND = hci_command_op_code(0x04, 0x000A) +HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND = hci_command_op_code(0x04, 0x000B) +HCI_READ_LOCAL_SIMPLE_PAIRING_OPTIONS_COMMAND = hci_command_op_code(0x04, 0x000C) +HCI_READ_LOCAL_SUPPORTED_CODECS_V2_COMMAND = hci_command_op_code(0x04, 0x000D) +HCI_READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES_COMMAND = hci_command_op_code(0x04, 0x000E) +HCI_READ_LOCAL_SUPPORTED_CONTROLLER_DELAY_COMMAND = hci_command_op_code(0x04, 0x000F) +HCI_READ_FAILED_CONTACT_COUNTER_COMMAND = hci_command_op_code(0x05, 0x0001) +HCI_RESET_FAILED_CONTACT_COUNTER_COMMAND = hci_command_op_code(0x05, 0x0002) +HCI_READ_LINK_QUALITY_COMMAND = hci_command_op_code(0x05, 0x0003) +HCI_READ_RSSI_COMMAND = hci_command_op_code(0x05, 0x0005) +HCI_READ_AFH_CHANNEL_MAP_COMMAND = hci_command_op_code(0x05, 0x0006) +HCI_READ_CLOCK_COMMAND = hci_command_op_code(0x05, 0x0007) +HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND = hci_command_op_code(0x05, 0x0008) +HCI_GET_MWS_TRANSPORT_LAYER_CONFIGURATION_COMMAND = hci_command_op_code(0x05, 0x000C) +HCI_SET_TRIGGERED_CLOCK_CAPTURE_COMMAND = hci_command_op_code(0x05, 0x000D) +HCI_READ_LOOPBACK_MODE_COMMAND = hci_command_op_code(0x06, 0x0001) +HCI_WRITE_LOOPBACK_MODE_COMMAND = hci_command_op_code(0x06, 0x0002) +HCI_ENABLE_DEVICE_UNDER_TEST_MODE_COMMAND = hci_command_op_code(0x06, 0x0003) +HCI_WRITE_SIMPLE_PAIRING_DEBUG_MODE_COMMAND = hci_command_op_code(0x06, 0x0004) +HCI_WRITE_SECURE_CONNECTIONS_TEST_MODE_COMMAND = hci_command_op_code(0x06, 0x000A) +HCI_LE_SET_EVENT_MASK_COMMAND = hci_command_op_code(0x08, 0x0001) +HCI_LE_READ_BUFFER_SIZE_COMMAND = hci_command_op_code(0x08, 0x0002) +HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0003) +HCI_LE_SET_RANDOM_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x0005) +HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0006) +HCI_LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER_COMMAND = hci_command_op_code(0x08, 0x0007) +HCI_LE_SET_ADVERTISING_DATA_COMMAND = hci_command_op_code(0x08, 0x0008) +HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0009) +HCI_LE_SET_ADVERTISING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x000A) +HCI_LE_SET_SCAN_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x000B) +HCI_LE_SET_SCAN_ENABLE_COMMAND = hci_command_op_code(0x08, 0x000C) +HCI_LE_CREATE_CONNECTION_COMMAND = hci_command_op_code(0x08, 0x000D) +HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND = hci_command_op_code(0x08, 0x000E) +HCI_LE_READ_FILTER_ACCEPT_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x000F) +HCI_LE_CLEAR_FILTER_ACCEPT_LIST_COMMAND = hci_command_op_code(0x08, 0x0010) +HCI_LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST_COMMAND = hci_command_op_code(0x08, 0x0011) +HCI_LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST_COMMAND = hci_command_op_code(0x08, 0x0012) +HCI_LE_CONNECTION_UPDATE_COMMAND = hci_command_op_code(0x08, 0x0013) +HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND = hci_command_op_code(0x08, 0x0014) +HCI_LE_READ_CHANNEL_MAP_COMMAND = hci_command_op_code(0x08, 0x0015) +HCI_LE_READ_REMOTE_FEATURES_COMMAND = hci_command_op_code(0x08, 0x0016) +HCI_LE_ENCRYPT_COMMAND = hci_command_op_code(0x08, 0x0017) +HCI_LE_RAND_COMMAND = hci_command_op_code(0x08, 0x0018) +HCI_LE_ENABLE_ENCRYPTION_COMMAND = hci_command_op_code(0x08, 0x0019) +HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND = hci_command_op_code(0x08, 0x001A) +HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x08, 0x001B) +HCI_LE_READ_SUPPORTED_STATES_COMMAND = hci_command_op_code(0x08, 0x001C) +HCI_LE_RECEIVER_TEST_COMMAND = hci_command_op_code(0x08, 0x001D) +HCI_LE_TRANSMITTER_TEST_COMMAND = hci_command_op_code(0x08, 0x001E) +HCI_LE_TEST_END_COMMAND = hci_command_op_code(0x08, 0x001F) +HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND = hci_command_op_code(0x08, 0x0020) +HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND = hci_command_op_code(0x08, 0x0021) +HCI_LE_SET_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x0022) +HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x0023) +HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x0024) +HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND = hci_command_op_code(0x08, 0x0025) +HCI_LE_GENERATE_DHKEY_COMMAND = hci_command_op_code(0x08, 0x0026) +HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND = hci_command_op_code(0x08, 0x0027) +HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND = hci_command_op_code(0x08, 0x0028) +HCI_LE_CLEAR_RESOLVING_LIST_COMMAND = hci_command_op_code(0x08, 0x0029) +HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x002A) +HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x002B) +HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x002C) +HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND = hci_command_op_code(0x08, 0x002D) +HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND = hci_command_op_code(0x08, 0x002E) +HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x002F) +HCI_LE_READ_PHY_COMMAND = hci_command_op_code(0x08, 0x0030) +HCI_LE_SET_DEFAULT_PHY_COMMAND = hci_command_op_code(0x08, 0x0031) +HCI_LE_SET_PHY_COMMAND = hci_command_op_code(0x08, 0x0032) +HCI_LE_RECEIVER_TEST_V2_COMMAND = hci_command_op_code(0x08, 0x0033) +HCI_LE_TRANSMITTER_TEST_V2_COMMAND = hci_command_op_code(0x08, 0x0034) +HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND = hci_command_op_code(0x08, 0x0035) +HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0036) +HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND = hci_command_op_code(0x08, 0x0037) +HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND = hci_command_op_code(0x08, 0x0038) +HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0039) +HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND = hci_command_op_code(0x08, 0x003A) +HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_COMMAND = hci_command_op_code(0x08, 0x003B) +HCI_LE_REMOVE_ADVERTISING_SET_COMMAND = hci_command_op_code(0x08, 0x003C) +HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND = hci_command_op_code(0x08, 0x003D) +HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x003E) +HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND = hci_command_op_code(0x08, 0x003F) +HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0040) +HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0041) +HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0042) +HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND = hci_command_op_code(0x08, 0x0043) +HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x0044) +HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND = hci_command_op_code(0x08, 0x0045) +HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x0046) +HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND = hci_command_op_code(0x08, 0x0047) +HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND = hci_command_op_code(0x08, 0x0048) +HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND = hci_command_op_code(0x08, 0x0049) +HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND = hci_command_op_code(0x08, 0x004A) +HCI_LE_READ_TRANSMIT_POWER_COMMAND = hci_command_op_code(0x08, 0x004B) +HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND = hci_command_op_code(0x08, 0x004C) +HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND = hci_command_op_code(0x08, 0x004D) +HCI_LE_SET_PRIVACY_MODE_COMMAND = hci_command_op_code(0x08, 0x004E) +HCI_LE_RECEIVER_TEST_V3_COMMAND = hci_command_op_code(0x08, 0x004F) +HCI_LE_TRANSMITTER_TEST_V3_COMMAND = hci_command_op_code(0x08, 0x0050) +HCI_LE_SET_CONNECTIONLESS_CTE_TRANSMIT_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0051) +HCI_LE_SET_CONNECTIONLESS_CTE_TRANSMIT_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0052) +HCI_LE_SET_CONNECTIONLESS_IQ_SAMPLING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0053) +HCI_LE_SET_CONNECTION_CTE_RECEIVE_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0054) +HCI_LE_SET_CONNECTION_CTE_TRANSMIT_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0055) +HCI_LE_CONNECTION_CTE_REQUEST_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0056) +HCI_LE_CONNECTION_CTE_RESPONSE_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0057) +HCI_LE_READ_ANTENNA_INFORMATION_COMMAND = hci_command_op_code(0x08, 0x0058) +HCI_LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0059) +HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_COMMAND = hci_command_op_code(0x08, 0x005A) +HCI_LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER_COMMAND = hci_command_op_code(0x08, 0x005B) +HCI_LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x005C) +HCI_LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x005D) +HCI_LE_GENERATE_DHKEY_V2_COMMAND = hci_command_op_code(0x08, 0x005E) +HCI_LE_MODIFY_SLEEP_CLOCK_ACCURACY_COMMAND = hci_command_op_code(0x08, 0x005F) +HCI_LE_READ_BUFFER_SIZE_V2_COMMAND = hci_command_op_code(0x08, 0x0060) +HCI_LE_READ_ISO_TX_SYNC_COMMAND = hci_command_op_code(0x08, 0x0061) +HCI_LE_SET_CIG_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0062) +HCI_LE_SET_CIG_PARAMETERS_TEST_COMMAND = hci_command_op_code(0x08, 0x0063) +HCI_LE_CREATE_CIS_COMMAND = hci_command_op_code(0x08, 0x0064) +HCI_LE_REMOVE_CIG_COMMAND = hci_command_op_code(0x08, 0x0065) +HCI_LE_ACCEPT_CIS_REQUEST_COMMAND = hci_command_op_code(0x08, 0x0066) +HCI_LE_REJECT_CIS_REQUEST_COMMAND = hci_command_op_code(0x08, 0x0067) +HCI_LE_CREATE_BIG_COMMAND = hci_command_op_code(0x08, 0x0068) +HCI_LE_CREATE_BIG_TEST_COMMAND = hci_command_op_code(0x08, 0x0069) +HCI_LE_TERMINATE_BIG_COMMAND = hci_command_op_code(0x08, 0x006A) +HCI_LE_BIG_CREATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x006B) +HCI_LE_BIG_TERMINATE_SYNC_COMMAND = hci_command_op_code(0x08, 0x006C) +HCI_LE_REQUEST_PEER_SCA_COMMAND = hci_command_op_code(0x08, 0x006D) +HCI_LE_SETUP_ISO_DATA_PATH_COMMAND = hci_command_op_code(0x08, 0x006E) +HCI_LE_REMOVE_ISO_DATA_PATH_COMMAND = hci_command_op_code(0x08, 0x006F) +HCI_LE_ISO_TRANSMIT_TEST_COMMAND = hci_command_op_code(0x08, 0x0070) +HCI_LE_ISO_RECEIVE_TEST_COMMAND = hci_command_op_code(0x08, 0x0071) +HCI_LE_ISO_READ_TEST_COUNTERS_COMMAND = hci_command_op_code(0x08, 0x0072) +HCI_LE_ISO_TEST_END_COMMAND = hci_command_op_code(0x08, 0x0073) +HCI_LE_SET_HOST_FEATURE_COMMAND = hci_command_op_code(0x08, 0x0074) +HCI_LE_READ_ISO_LINK_QUALITY_COMMAND = hci_command_op_code(0x08, 0x0075) +HCI_LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x08, 0x0076) +HCI_LE_READ_REMOTE_TRANSMIT_POWER_LEVEL_COMMAND = hci_command_op_code(0x08, 0x0077) +HCI_LE_SET_PATH_LOSS_REPORTING_PARAMETERS_COMMAND = hci_command_op_code(0x08, 0x0078) +HCI_LE_SET_PATH_LOSS_REPORTING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x0079) +HCI_LE_SET_TRANSMIT_POWER_REPORTING_ENABLE_COMMAND = hci_command_op_code(0x08, 0x007A) +HCI_LE_TRANSMITTER_TEST_V4_COMMAND = hci_command_op_code(0x08, 0x007B) +HCI_LE_SET_DATA_RELATED_ADDRESS_CHANGES_COMMAND = hci_command_op_code(0x08, 0x007C) +HCI_LE_SET_DEFAULT_SUBRATE_COMMAND = hci_command_op_code(0x08, 0x007D) +HCI_LE_SUBRATE_REQUEST_COMMAND = hci_command_op_code(0x08, 0x007E) HCI_COMMAND_NAMES = { - HCI_INQUIRY_COMMAND: 'HCI_INQUIRY_COMMAND', - HCI_INQUIRY_CANCEL_COMMAND: 'HCI_INQUIRY_CANCEL_COMMAND', - HCI_CREATE_CONNECTION_COMMAND: 'HCI_CREATE_CONNECTION_COMMAND', - HCI_DISCONNECT_COMMAND: 'HCI_DISCONNECT_COMMAND', - HCI_ACCEPT_CONNECTION_REQUEST_COMMAND: 'HCI_ACCEPT_CONNECTION_REQUEST_COMMAND', - HCI_LINK_KEY_REQUEST_REPLY_COMMAND: 'HCI_LINK_KEY_REQUEST_REPLY_COMMAND', - HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND: 'HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND', - HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND: 'HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND', - HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND: 'HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND', - HCI_AUTHENTICATION_REQUESTED_COMMAND: 'HCI_AUTHENTICATION_REQUESTED_COMMAND', - HCI_SET_CONNECTION_ENCRYPTION_COMMAND: 'HCI_SET_CONNECTION_ENCRYPTION_COMMAND', - HCI_REMOTE_NAME_REQUEST_COMMAND: 'HCI_REMOTE_NAME_REQUEST_COMMAND', - HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND: 'HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND', - HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND: 'HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND', - HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND: 'HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND', - HCI_READ_CLOCK_OFFSET_COMMAND: 'HCI_READ_CLOCK_OFFSET_COMMAND', - HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND: 'HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND', - HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND: 'HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND', - HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND: 'HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND', - HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND: 'HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND', - HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND: 'HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND', - HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND: 'HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND', - HCI_SNIFF_MODE_COMMAND: 'HCI_SNIFF_MODE_COMMAND', - HCI_EXIT_SNIFF_MODE_COMMAND: 'HCI_EXIT_SNIFF_MODE_COMMAND', - HCI_SWITCH_ROLE_COMMAND: 'HCI_SWITCH_ROLE_COMMAND', - HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND: 'HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND', - HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND: 'HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND', - HCI_SNIFF_SUBRATING_COMMAND: 'HCI_SNIFF_SUBRATING_COMMAND', - HCI_SET_EVENT_MASK_COMMAND: 'HCI_SET_EVENT_MASK_COMMAND', - HCI_RESET_COMMAND: 'HCI_RESET_COMMAND', - HCI_SET_EVENT_FILTER_COMMAND: 'HCI_SET_EVENT_FILTER_COMMAND', - HCI_READ_STORED_LINK_KEY_COMMAND: 'HCI_READ_STORED_LINK_KEY_COMMAND', - HCI_DELETE_STORED_LINK_KEY_COMMAND: 'HCI_DELETE_STORED_LINK_KEY_COMMAND', - HCI_WRITE_LOCAL_NAME_COMMAND: 'HCI_WRITE_LOCAL_NAME_COMMAND', - HCI_READ_LOCAL_NAME_COMMAND: 'HCI_READ_LOCAL_NAME_COMMAND', - HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND: 'HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND', - HCI_WRITE_PAGE_TIMEOUT_COMMAND: 'HCI_WRITE_PAGE_TIMEOUT_COMMAND', - HCI_WRITE_SCAN_ENABLE_COMMAND: 'HCI_WRITE_SCAN_ENABLE_COMMAND', - HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND: 'HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND', - HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND: 'HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND', - HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND: 'HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND', - HCI_READ_CLASS_OF_DEVICE_COMMAND: 'HCI_READ_CLASS_OF_DEVICE_COMMAND', - HCI_WRITE_CLASS_OF_DEVICE_COMMAND: 'HCI_WRITE_CLASS_OF_DEVICE_COMMAND', - HCI_READ_VOICE_SETTING_COMMAND: 'HCI_READ_VOICE_SETTING_COMMAND', - HCI_WRITE_VOICE_SETTING_COMMAND: 'HCI_WRITE_VOICE_SETTING_COMMAND', - HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND: 'HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND', - HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND: 'HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND', - HCI_HOST_BUFFER_SIZE_COMMAND: 'HCI_HOST_BUFFER_SIZE_COMMAND', - HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND: 'HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND', - HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND: 'HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND', - HCI_READ_CURRENT_IAC_LAP_COMMAND: 'HCI_READ_CURRENT_IAC_LAP_COMMAND', - HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND: 'HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND', - HCI_WRITE_INQUIRY_MODE_COMMAND: 'HCI_WRITE_INQUIRY_MODE_COMMAND', - HCI_READ_PAGE_SCAN_TYPE_COMMAND: 'HCI_READ_PAGE_SCAN_TYPE_COMMAND', - HCI_WRITE_PAGE_SCAN_TYPE_COMMAND: 'HCI_WRITE_PAGE_SCAN_TYPE_COMMAND', - HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND: 'HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND', - HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND: 'HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND', - HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND: 'HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND', - HCI_SET_EVENT_MASK_PAGE_2_COMMAND: 'HCI_SET_EVENT_MASK_PAGE_2_COMMAND', - HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND: 'HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND', - HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND: 'HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND', - HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND: 'HCI_READ_LOCAL_SUPPORTED_COMMANDS_COMMAND', - HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND: 'HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND', - HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND: 'HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND', - HCI_READ_BUFFER_SIZE_COMMAND: 'HCI_READ_BUFFER_SIZE_COMMAND', - HCI_READ_LE_HOST_SUPPORT_COMMAND: 'HCI_READ_LE_HOST_SUPPORT_COMMAND', - HCI_WRITE_LE_HOST_SUPPORT_COMMAND: 'HCI_WRITE_LE_HOST_SUPPORT_COMMAND', - HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND: 'HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND', - HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND: 'HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND', - HCI_READ_BD_ADDR_COMMAND: 'HCI_READ_BD_ADDR_COMMAND', - HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND: 'HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND', - HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND: 'HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND', - HCI_LE_SET_EVENT_MASK_COMMAND: 'HCI_LE_SET_EVENT_MASK_COMMAND', - HCI_LE_READ_BUFFER_SIZE_COMMAND: 'HCI_LE_READ_BUFFER_SIZE_COMMAND', - HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND: 'HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND', - HCI_LE_SET_RANDOM_ADDRESS_COMMAND: 'HCI_LE_SET_RANDOM_ADDRESS_COMMAND', - HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND: 'HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND', - HCI_LE_READ_ADVERTISING_CHANNEL_TX_POWER_COMMAND: 'HCI_LE_READ_ADVERTISING_CHANNEL_TX_POWER_COMMAND', - HCI_LE_SET_ADVERTISING_DATA_COMMAND: 'HCI_LE_SET_ADVERTISING_DATA_COMMAND', - HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND: 'HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND', - HCI_LE_SET_ADVERTISING_ENABLE_COMMAND: 'HCI_LE_SET_ADVERTISING_ENABLE_COMMAND', - HCI_LE_SET_SCAN_PARAMETERS_COMMAND: 'HCI_LE_SET_SCAN_PARAMETERS_COMMAND', - HCI_LE_SET_SCAN_ENABLE_COMMAND: 'HCI_LE_SET_SCAN_ENABLE_COMMAND', - HCI_LE_CREATE_CONNECTION_COMMAND: 'HCI_LE_CREATE_CONNECTION_COMMAND', - HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND: 'HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND', - HCI_LE_READ_WHITE_LIST_SIZE_COMMAND: 'HCI_LE_READ_WHITE_LIST_SIZE_COMMAND', - HCI_LE_CLEAR_WHITE_LIST_COMMAND: 'HCI_LE_CLEAR_WHITE_LIST_COMMAND', - HCI_LE_ADD_DEVICE_TO_WHITE_LIST_COMMAND: 'HCI_LE_ADD_DEVICE_TO_WHITE_LIST_COMMAND', - HCI_LE_REMOVE_DEVICE_FROM_WHITE_LIST_COMMAND: 'HCI_LE_REMOVE_DEVICE_FROM_WHITE_LIST_COMMAND', - HCI_LE_CONNECTION_UPDATE_COMMAND: 'HCI_LE_CONNECTION_UPDATE_COMMAND', - HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND: 'HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND', - HCI_LE_READ_CHANNEL_MAP_COMMAND: 'HCI_LE_READ_CHANNEL_MAP_COMMAND', - HCI_LE_READ_REMOTE_FEATURES_COMMAND: 'HCI_LE_READ_REMOTE_FEATURES_COMMAND', - HCI_LE_ENCRYPT_COMMAND: 'HCI_LE_ENCRYPT_COMMAND', - HCI_LE_RAND_COMMAND: 'HCI_LE_RAND_COMMAND', - HCI_LE_START_ENCRYPTION_COMMAND: 'HCI_LE_START_ENCRYPTION_COMMAND', - HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND: 'HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND', - HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND: 'HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND', - HCI_LE_READ_SUPPORTED_STATES_COMMAND: 'HCI_LE_READ_SUPPORTED_STATES_COMMAND', - HCI_LE_RECEIVER_TEST_COMMAND: 'HCI_LE_RECEIVER_TEST_COMMAND', - HCI_LE_TRANSMITTER_TEST_COMMAND: 'HCI_LE_TRANSMITTER_TEST_COMMAND', - HCI_LE_TEST_END_COMMAND: 'HCI_LE_TEST_END_COMMAND', - HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND: 'HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND', - HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND: 'HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND', - HCI_LE_SET_DATA_LENGTH_COMMAND: 'HCI_LE_SET_DATA_LENGTH_COMMAND', - HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND: 'HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND', - HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND: 'HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND', - HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND: 'HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND', - HCI_LE_GENERATE_DHKEY_COMMAND: 'HCI_LE_GENERATE_DHKEY_COMMAND', - HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND: 'HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND', - HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND: 'HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND', - HCI_LE_CLEAR_RESOLVING_LIST_COMMAND: 'HCI_LE_CLEAR_RESOLVING_LIST_COMMAND', - HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND: 'HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND', - HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND: 'HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND', - HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND: 'HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND', - HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND: 'HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND', - HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND: 'HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND', - HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND: 'HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND', - HCI_LE_READ_PHY_COMMAND: 'HCI_LE_READ_PHY_COMMAND', - HCI_LE_SET_DEFAULT_PHY_COMMAND: 'HCI_LE_SET_DEFAULT_PHY_COMMAND', - HCI_LE_SET_PHY_COMMAND: 'HCI_LE_SET_PHY_COMMAND', - HCI_LE_ENHANCED_RECEIVER_TEST_COMMAND: 'HCI_LE_ENHANCED_RECEIVER_TEST_COMMAND', - HCI_LE_ENHANCED_TRANSMITTER_TEST_COMMAND: 'HCI_LE_ENHANCED_TRANSMITTER_TEST_COMMAND', - HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND: 'HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND', - HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND: 'HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND', - HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND: 'HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND', - HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND: 'HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND', - HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND: 'HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND', - HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND: 'HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND', - HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERETISING_SETS_COMMAND: 'HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERETISING_SETS_COMMAND', - HCI_LE_REMOVE_ADVERTISING_SET_COMMAND: 'HCI_LE_REMOVE_ADVERTISING_SET_COMMAND', - HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND: 'HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND', - HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND: 'HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND', - HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND: 'HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND', - HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND: 'HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND', - HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND: 'HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND', - HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND: 'HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND', - HCI_LE_SET_EXTENDED_CREATE_CONNECTION_COMMAND: 'HCI_LE_SET_EXTENDED_CREATE_CONNECTION_COMMAND', - HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND: 'HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND', - HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND: 'HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND', - HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND: 'HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND', - HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND: 'HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND', - HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND: 'HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND', - HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND: 'HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND', - HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND: 'HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND', - HCI_LE_READ_TRANSMIT_POWER_COMMAND: 'HCI_LE_READ_TRANSMIT_POWER_COMMAND', - HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND: 'HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND', - HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND: 'HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND', - HCI_LE_SET_PRIVACY_MODE_COMMAND: 'HCI_LE_SET_PRIVACY_MODE_COMMAND' + command_code: command_name for (command_name, command_code) in globals().items() + if command_name.startswith('HCI_') and command_name.endswith('_COMMAND') } - # HCI Error Codes # See Bluetooth spec Vol 2, Part D - 1.3 LIST OF ERROR CODES -HCI_SUCCESS = 0x00 -HCI_UNKNOWN_HCI_COMMAND_ERROR = 0x01 -HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR = 0x02 -HCI_HARDWARE_FAILURE_ERROR = 0x03 -HCI_PAGE_TIMEOUT_ERROR = 0x04 -HCI_AUTHENTICATION_FAILURE_ERROR = 0x05 -HCI_PIN_OR_KEY_MISSING_ERROR = 0x06 -HCI_MEMORY_CAPACITY_EXCEEDED_ERROR = 0x07 -HCI_CONNECTION_TIMEOUT_ERROR = 0x08 -HCI_CONNECTION_LIMIT_EXCEEDED_ERROR = 0x09 -HCI_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED_ERROR = 0x0A -HCI_CONNECTION_ALREADY_EXISTS_ERROR = 0x0B -HCI_COMMAND_DISALLOWED_ERROR = 0x0C -HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR = 0x0D -HCI_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS_ERROR = 0x0E -HCI_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR_ERROR = 0x0F -HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR = 0x10 -HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE_ERROR = 0x11 -HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR = 0x12 -HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR = 0x13 -HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES_ERROR = 0x14 -HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF_ERROR = 0x15 -HCI_CONNECTION_TERMINATED_BY_LOCAL_HOST_ERROR = 0x16 -HCI_UNACCEPTABLE_CONNECTION_PARAMETERS_ERROR = 0x3B -HCI_CONNECTION_FAILED_TO_BE_ESTABLISHED_ERROR = 0x3E -# TODO: more error codes +HCI_SUCCESS = 0x00 +HCI_UNKNOWN_HCI_COMMAND_ERROR = 0x01 +HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR = 0x02 +HCI_HARDWARE_FAILURE_ERROR = 0x03 +HCI_PAGE_TIMEOUT_ERROR = 0x04 +HCI_AUTHENTICATION_FAILURE_ERROR = 0x05 +HCI_PIN_OR_KEY_MISSING_ERROR = 0x06 +HCI_MEMORY_CAPACITY_EXCEEDED_ERROR = 0x07 +HCI_CONNECTION_TIMEOUT_ERROR = 0x08 +HCI_CONNECTION_LIMIT_EXCEEDED_ERROR = 0x09 +HCI_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED_ERROR = 0x0A +HCI_CONNECTION_ALREADY_EXISTS_ERROR = 0x0B +HCI_COMMAND_DISALLOWED_ERROR = 0x0C +HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR = 0x0D +HCI_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS_ERROR = 0x0E +HCI_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR_ERROR = 0x0F +HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR = 0x10 +HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE_ERROR = 0x11 +HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR = 0x12 +HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR = 0x13 +HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES_ERROR = 0x14 +HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF_ERROR = 0x15 +HCI_CONNECTION_TERMINATED_BY_LOCAL_HOST_ERROR = 0x16 +HCI_REPEATED_ATTEMPTS_ERROR = 0X17 +HCI_PAIRING_NOT_ALLOWED_ERROR = 0X18 +HCI_UNKNOWN_LMP_PDU_ERROR = 0X19 +HCI_UNSUPPORTED_REMOTE_FEATURE_ERROR = 0X1A +HCI_SCO_OFFSET_REJECTED_ERROR = 0X1B +HCI_SCO_INTERVAL_REJECTED_ERROR = 0X1C +HCI_SCO_AIR_MODE_REJECTED_ERROR = 0X1D +HCI_INVALID_LMP_OR_LL_PARAMETERS_ERROR = 0X1E +HCI_UNSPECIFIED_ERROR_ERROR = 0X1F +HCI_UNSUPPORTED_LMP_OR_LL_PARAMETER_VALUE_ERROR = 0X20 +HCI_ROLE_CHANGE_NOT_ALLOWED_ERROR = 0X21 +HCI_LMP_OR_LL_RESPONSE_TIMEOUT_ERROR = 0X22 +HCI_LMP_ERROR_TRANSACTION_COLLISION_OR_LL_PROCEDURE_COLLISION_ERROR = 0X23 +HCI_LMP_PDU_NOT_ALLOWED_ERROR = 0X24 +HCI_ENCRYPTION_MODE_NOT_ACCEPTABLE_ERROR = 0X25 +HCI_LINK_KEY_CANNOT_BE_CHANGED_ERROR = 0X26 +HCI_REQUESTED_QOS_NOT_SUPPORTED_ERROR = 0X27 +HCI_INSTANT_PASSED_ERROR = 0X28 +HCI_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED_ERROR = 0X29 +HCI_DIFFERENT_TRANSACTION_COLLISION_ERROR = 0X2A +HCI_RESERVED_FOR_FUTURE_USE = 0X2B +HCI_QOS_UNACCEPTABLE_PARAMETER_ERROR = 0X2C +HCI_QOS_REJECTED_ERROR = 0X2D +HCI_CHANNEL_CLASSIFICATION_NOT_SUPPORTED_ERROR = 0X2E +HCI_INSUFFICIENT_SECURITY_ERROR = 0X2F +HCI_PARAMETER_OUT_OF_MANDATORY_RANGE_ERROR = 0X30 +HCI_ROLE_SWITCH_PENDING_ERROR = 0X32 +HCI_RESERVED_SLOT_VIOLATION_ERROR = 0X34 +HCI_ROLE_SWITCH_FAILED_ERROR = 0X35 +HCI_EXTENDED_INQUIRY_RESPONSE_TOO_LARGE_ERROR = 0X36 +HCI_SECURE_SIMPLE_PAIRING_NOT_SUPPORTED_BY_HOST_ERROR = 0X37 +HCI_HOST_BUSY_PAIRING_ERROR = 0X38 +HCI_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND_ERROR = 0X39 +HCI_CONTROLLER_BUSY_ERROR = 0X3A +HCI_UNACCEPTABLE_CONNECTION_PARAMETERS_ERROR = 0X3B +HCI_ADVERTISING_TIMEOUT_ERROR = 0X3C +HCI_CONNECTION_TERMINATED_DUE_TO_MIC_FAILURE_ERROR = 0X3D +HCI_CONNECTION_FAILED_TO_BE_ESTABLISHED_ERROR = 0X3E +HCI_COARSE_CLOCK_ADJUSTMENT_REJECTED_BUT_WILL_TRY_TO_ADJUST_USING_CLOCK_DRAGGING_ERROR = 0X40 +HCI_TYPE0_SUBMAP_NOT_DEFINED_ERROR = 0X41 +HCI_UNKNOWN_ADVERTISING_IDENTIFIER_ERROR = 0X42 +HCI_LIMIT_REACHED_ERROR = 0X43 +HCI_OPERATION_CANCELLED_BY_HOST_ERROR = 0X44 +HCI_PACKET_TOO_LONG_ERROR = 0X45 HCI_ERROR_NAMES = { - HCI_SUCCESS: 'HCI_SUCCESS', - HCI_UNKNOWN_HCI_COMMAND_ERROR: 'HCI_UNKNOWN_HCI_COMMAND_ERROR', - HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR: 'HCI_UNKNOWN_CONNECTION_IDENTIFIER_ERROR', - HCI_HARDWARE_FAILURE_ERROR: 'HCI_HARDWARE_FAILURE_ERROR', - HCI_PAGE_TIMEOUT_ERROR: 'HCI_PAGE_TIMEOUT_ERROR', - HCI_AUTHENTICATION_FAILURE_ERROR: 'HCI_AUTHENTICATION_FAILURE_ERROR', - HCI_PIN_OR_KEY_MISSING_ERROR: 'HCI_PIN_OR_KEY_MISSING_ERROR', - HCI_MEMORY_CAPACITY_EXCEEDED_ERROR: 'HCI_MEMORY_CAPACITY_EXCEEDED_ERROR', - HCI_CONNECTION_TIMEOUT_ERROR: 'HCI_CONNECTION_TIMEOUT_ERROR', - HCI_CONNECTION_LIMIT_EXCEEDED_ERROR: 'HCI_CONNECTION_LIMIT_EXCEEDED_ERROR', - HCI_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED_ERROR: 'HCI_SYNCHRONOUS_CONNECTION_LIMIT_TO_A_DEVICE_EXCEEDED_ERROR', - HCI_CONNECTION_ALREADY_EXISTS_ERROR: 'HCI_CONNECTION_ALREADY_EXISTS_ERROR', - HCI_COMMAND_DISALLOWED_ERROR: 'HCI_COMMAND_DISALLOWED_ERROR', - HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR: 'HCI_CONNECTION_REJECTED_DUE_TO_LIMITED_RESOURCES_ERROR', - HCI_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS_ERROR: 'HCI_CONNECTION_REJECTED_DUE_TO_SECURITY_REASONS_ERROR', - HCI_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR_ERROR: 'HCI_CONNECTION_REJECTED_DUE_TO_UNACCEPTABLE_BD_ADDR_ERROR', - HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR: 'HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR', - HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE_ERROR: 'HCI_UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE_ERROR', - HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR: 'HCI_INVALID_HCI_COMMAND_PARAMETERS_ERROR', - HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR: 'HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR', - HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES_ERROR: 'HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_LOW_RESOURCES_ERROR', - HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF_ERROR: 'HCI_REMOTE_DEVICE_TERMINATED_CONNECTION_DUE_TO_POWER_OFF_ERROR', - HCI_CONNECTION_TERMINATED_BY_LOCAL_HOST_ERROR: 'HCI_CONNECTION_TERMINATED_BY_LOCAL_HOST_ERROR', - HCI_UNACCEPTABLE_CONNECTION_PARAMETERS_ERROR: 'HCI_UNACCEPTABLE_CONNECTION_PARAMETERS_ERROR', - HCI_CONNECTION_FAILED_TO_BE_ESTABLISHED_ERROR: 'HCI_CONNECTION_FAILED_TO_BE_ESTABLISHED_ERROR' + error_code: error_name for (error_name, error_code) in globals().items() + if error_name.startswith('HCI_') and error_name.endswith('_ERROR') } +HCI_ERROR_NAMES[HCI_SUCCESS] = 'HCI_SUCCESS' # Command Status codes HCI_COMMAND_STATUS_PENDING = 0 # LE Event Masks -LE_CONNECTION_COMPLETE_EVENT_MASK = (1 << 0) -LE_ADVERTISING_REPORT_EVENT_MASK = (1 << 1) -LE_CONNECTION_UPDATE_COMPLETE_EVENT_MASK = (1 << 2) -LE_READ_REMOTE_FEATURES_COMPLETE_EVENT_MASK = (1 << 3) -LE_LONG_TERM_KEY_REQUEST_EVENT_MASK = (1 << 4) -LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT_MASK = (1 << 5) -LE_DATA_LENGTH_CHANGE_EVENT_MASK = (1 << 6) -LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT_MASK = (1 << 7) -LE_GENERATE_DHKEY_COMPLETE_EVENT_MASK = (1 << 8) -LE_ENHANCED_CONNECTION_COMPLETE_EVENT_MASK = (1 << 9) -LE_DIRECTED_ADVERTISING_REPORT_EVENT_MASK = (1 << 10) -LE_PHY_UPDATE_COMPLETE_EVENT_MASK = (1 << 11) -LE_EXTENDED_ADVERTISING_REPORT_EVENT_MASK = (1 << 12) -LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT_MASK = (1 << 13) -LE_PERIODIC_ADVERTISING_REPORT_EVENT_MASK = (1 << 14) -LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT_MASK = (1 << 15) -LE_EXTENDED_SCAN_TIMEOUT_EVENT_MASK = (1 << 16) -LE_EXTENDED_ADVERTISING_SET_TERMINATED_EVENT_MASK = (1 << 17) -LE_SCAN_REQUEST_RECEIVED_EVENT_MASK = (1 << 18) -LE_CHANNEL_SELECTION_ALGORITHM_EVENT_MASK = (1 << 19) +HCI_LE_CONNECTION_COMPLETE_EVENT_MASK = (1 << 0) +HCI_LE_ADVERTISING_REPORT_EVENT_MASK = (1 << 1) +HCI_LE_CONNECTION_UPDATE_COMPLETE_EVENT_MASK = (1 << 2) +HCI_LE_READ_REMOTE_FEATURES_COMPLETE_EVENT_MASK = (1 << 3) +HCI_LE_LONG_TERM_KEY_REQUEST_EVENT_MASK = (1 << 4) +HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_EVENT_MASK = (1 << 5) +HCI_LE_DATA_LENGTH_CHANGE_EVENT_MASK = (1 << 6) +HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMPLETE_EVENT_MASK = (1 << 7) +HCI_LE_GENERATE_DHKEY_COMPLETE_EVENT_MASK = (1 << 8) +HCI_LE_ENHANCED_CONNECTION_COMPLETE_EVENT_MASK = (1 << 9) +HCI_LE_DIRECTED_ADVERTISING_REPORT_EVENT_MASK = (1 << 10) +HCI_LE_PHY_UPDATE_COMPLETE_EVENT_MASK = (1 << 11) +HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT_MASK = (1 << 12) +HCI_LE_PERIODIC_ADVERTISING_SYNC_ESTABLISHED_EVENT_MASK = (1 << 13) +HCI_LE_PERIODIC_ADVERTISING_REPORT_EVENT_MASK = (1 << 14) +HCI_LE_PERIODIC_ADVERTISING_SYNC_LOST_EVENT_MASK = (1 << 15) +HCI_LE_EXTENDED_SCAN_TIMEOUT_EVENT_MASK = (1 << 16) +HCI_LE_EXTENDED_ADVERTISING_SET_TERMINATED_EVENT_MASK = (1 << 17) +HCI_LE_SCAN_REQUEST_RECEIVED_EVENT_MASK = (1 << 18) +HCI_LE_CHANNEL_SELECTION_ALGORITHM_EVENT_MASK = (1 << 19) +HCI_LE_CONNECTIONLESS_IQ_REPORT_EVENT_MASK = (1 << 20) +HCI_LE_CONNECTION_IQ_REPORT_EVENT_MASK = (1 << 21) +HCI_LE_CTE_REQUEST_FAILED_EVENT_MASK = (1 << 22) +HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED_EVENT_MASK = (1 << 23) +HCI_LE_CIS_ESTABLISHED_EVENT_MASK = (1 << 24) +HCI_LE_CIS_REQUEST_EVENT_MASK = (1 << 25) +HCI_LE_CREATE_BIG_COMPLETE_EVENT_MASK = (1 << 26) +HCI_LE_TERMINATE_BIG_COMPLETE_EVENT_MASK = (1 << 27) +HCI_LE_BIG_SYNC_ESTABLISHED_EVENT_MASK = (1 << 28) +HCI_LE_BIG_SYNC_LOST_EVENT_MASK = (1 << 29) +HCI_LE_REQUEST_PEER_SCA_COMPLETE_EVENT_MASK = (1 << 30) +HCI_LE_PATH_LOSS_THRESHOLD_EVENT_MASK = (1 << 31) +HCI_LE_TRANSMIT_POWER_REPORTING_EVENT_MASK = (1 << 32) +HCI_LE_BIGINFO_ADVERTISING_REPORT_EVENT_MASK = (1 << 33) +HCI_LE_SUBRATE_CHANGE_EVENT_MASK = (1 << 34) + +HCI_LE_EVENT_MASK_NAMES = { + mask: mask_name for (mask_name, mask) in globals().items() + if mask_name.startswith('HCI_LE_') and mask_name.endswith('_EVENT_MASK') +} # ACL HCI_ACL_PB_FIRST_NON_FLUSHABLE = 0 @@ -649,10 +705,22 @@ HCI_LE_CODED_PHY = 3 HCI_LE_PHY_NAMES = { HCI_LE_1M_PHY: 'LE 1M', - HCI_LE_2M_PHY: 'L2 2M', + HCI_LE_2M_PHY: 'LE 2M', HCI_LE_CODED_PHY: 'LE Coded' } +HCI_LE_1M_PHY_BIT = 0 +HCI_LE_2M_PHY_BIT = 1 +HCI_LE_CODED_PHY_BIT = 2 + +HCI_LE_PHY_BIT_NAMES = ['LE_1M_PHY', 'LE_2M_PHY', 'LE_CODED_PHY'] + +HCI_LE_PHY_TYPE_TO_BIT = { + HCI_LE_1M_PHY: HCI_LE_1M_PHY_BIT, + HCI_LE_2M_PHY: HCI_LE_2M_PHY_BIT, + HCI_LE_CODED_PHY: HCI_LE_CODED_PHY_BIT +} + # Connection Parameters HCI_CONNECTION_INTERVAL_MS_PER_UNIT = 1.25 HCI_CONNECTION_LATENCY_MS_PER_UNIT = 1.25 @@ -735,7 +803,581 @@ HCI_RANDOM_DEVICE_ADDRESS_TYPE = 0x01 HCI_PUBLIC_IDENTITY_ADDRESS_TYPE = 0x02 HCI_RANDOM_IDENTITY_ADDRESS_TYPE = 0x03 +# Supported Commands Flags +# See Bluetooth spec @ 6.27 SUPPORTED COMMANDS +HCI_SUPPORTED_COMMANDS_FLAGS = ( + # Octet 0 + ( + HCI_INQUIRY_COMMAND, + HCI_INQUIRY_CANCEL_COMMAND, + HCI_PERIODIC_INQUIRY_MODE_COMMAND, + HCI_EXIT_PERIODIC_INQUIRY_MODE_COMMAND, + HCI_CREATE_CONNECTION_COMMAND, + HCI_DISCONNECT_COMMAND, + None, + HCI_CREATE_CONNECTION_CANCEL_COMMAND + ), + # Octet 1 + ( + HCI_ACCEPT_CONNECTION_REQUEST_COMMAND, + HCI_REJECT_CONNECTION_REQUEST_COMMAND, + HCI_LINK_KEY_REQUEST_REPLY_COMMAND, + HCI_LINK_KEY_REQUEST_NEGATIVE_REPLY_COMMAND, + HCI_PIN_CODE_REQUEST_REPLY_COMMAND, + HCI_PIN_CODE_REQUEST_NEGATIVE_REPLY_COMMAND, + HCI_CHANGE_CONNECTION_PACKET_TYPE_COMMAND, + HCI_AUTHENTICATION_REQUESTED_COMMAND + ), + # Octet 2 + ( + HCI_SET_CONNECTION_ENCRYPTION_COMMAND, + HCI_CHANGE_CONNECTION_LINK_KEY_COMMAND, + HCI_LINK_KEY_SELECTION_COMMAND, + HCI_REMOTE_NAME_REQUEST_COMMAND, + HCI_REMOTE_NAME_REQUEST_CANCEL_COMMAND, + HCI_READ_REMOTE_SUPPORTED_FEATURES_COMMAND, + HCI_READ_REMOTE_EXTENDED_FEATURES_COMMAND, + HCI_READ_REMOTE_VERSION_INFORMATION_COMMAND + ), + # Octet 3 + ( + HCI_READ_CLOCK_OFFSET_COMMAND, + HCI_READ_LMP_HANDLE_COMMAND, + None, + None, + None, + None, + None, + None + ), + # Octet 4 + ( + None, + HCI_HOLD_MODE_COMMAND, + HCI_SNIFF_MODE_COMMAND, + HCI_EXIT_SNIFF_MODE_COMMAND, + None, + None, + HCI_QOS_SETUP_COMMAND, + HCI_ROLE_DISCOVERY_COMMAND + ), + # Octet 5 + ( + HCI_SWITCH_ROLE_COMMAND, + HCI_READ_LINK_POLICY_SETTINGS_COMMAND, + HCI_WRITE_LINK_POLICY_SETTINGS_COMMAND, + HCI_READ_DEFAULT_LINK_POLICY_SETTINGS_COMMAND, + HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_COMMAND, + HCI_FLOW_SPECIFICATION_COMMAND, + HCI_SET_EVENT_MASK_COMMAND, + HCI_RESET_COMMAND + ), + # Octet 6 + ( + HCI_SET_EVENT_FILTER_COMMAND, + HCI_FLUSH_COMMAND, + HCI_READ_PIN_TYPE_COMMAND, + HCI_WRITE_PIN_TYPE_COMMAND, + None, + HCI_READ_STORED_LINK_KEY_COMMAND, + HCI_WRITE_STORED_LINK_KEY_COMMAND, + HCI_DELETE_STORED_LINK_KEY_COMMAND + ), + # Octet 7 + ( + HCI_WRITE_LOCAL_NAME_COMMAND, + HCI_READ_LOCAL_NAME_COMMAND, + HCI_READ_CONNECTION_ACCEPT_TIMEOUT_COMMAND, + HCI_WRITE_CONNECTION_ACCEPT_TIMEOUT_COMMAND, + HCI_READ_PAGE_TIMEOUT_COMMAND, + HCI_WRITE_PAGE_TIMEOUT_COMMAND, + HCI_READ_SCAN_ENABLE_COMMAND, + HCI_WRITE_SCAN_ENABLE_COMMAND + ), + # Octet 8 + ( + HCI_READ_PAGE_SCAN_ACTIVITY_COMMAND, + HCI_WRITE_PAGE_SCAN_ACTIVITY_COMMAND, + HCI_READ_INQUIRY_SCAN_ACTIVITY_COMMAND, + HCI_WRITE_INQUIRY_SCAN_ACTIVITY_COMMAND, + HCI_READ_AUTHENTICATION_ENABLE_COMMAND, + HCI_WRITE_AUTHENTICATION_ENABLE_COMMAND, + None, + None + ), + # Octet 9 + ( + HCI_READ_CLASS_OF_DEVICE_COMMAND, + HCI_WRITE_CLASS_OF_DEVICE_COMMAND, + HCI_READ_VOICE_SETTING_COMMAND, + HCI_WRITE_VOICE_SETTING_COMMAND, + HCI_READ_AUTOMATIC_FLUSH_TIMEOUT_COMMAND, + HCI_WRITE_AUTOMATIC_FLUSH_TIMEOUT_COMMAND, + HCI_READ_NUM_BROADCAST_RETRANSMISSIONS_COMMAND, + HCI_WRITE_NUM_BROADCAST_RETRANSMISSIONS_COMMAND + ), + # Octet 10 + ( + HCI_READ_HOLD_MODE_ACTIVITY_COMMAND, + HCI_WRITE_HOLD_MODE_ACTIVITY_COMMAND, + HCI_READ_TRANSMIT_POWER_LEVEL_COMMAND, + HCI_READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND, + HCI_WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE_COMMAND, + HCI_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_COMMAND, + HCI_HOST_BUFFER_SIZE_COMMAND, + HCI_HOST_NUMBER_OF_COMPLETED_PACKETS_COMMAND + ), + # Octet 11 + ( + HCI_READ_LINK_SUPERVISION_TIMEOUT_COMMAND, + HCI_WRITE_LINK_SUPERVISION_TIMEOUT_COMMAND, + HCI_READ_NUMBER_OF_SUPPORTED_IAC_COMMAND, + HCI_READ_CURRENT_IAC_LAP_COMMAND, + HCI_WRITE_CURRENT_IAC_LAP_COMMAND, + None, + None, + None + ), + # Octet 12 + ( + None, + HCI_SET_AFH_HOST_CHANNEL_CLASSIFICATION_COMMAND, + None, + None, + HCI_READ_INQUIRY_SCAN_TYPE_COMMAND, + HCI_WRITE_INQUIRY_SCAN_TYPE_COMMAND, + HCI_READ_INQUIRY_MODE_COMMAND, + HCI_WRITE_INQUIRY_MODE_COMMAND + ), + # Octet 13 + ( + HCI_READ_PAGE_SCAN_TYPE_COMMAND, + HCI_WRITE_PAGE_SCAN_TYPE_COMMAND, + HCI_READ_AFH_CHANNEL_ASSESSMENT_MODE_COMMAND, + HCI_WRITE_AFH_CHANNEL_ASSESSMENT_MODE_COMMAND, + None, + None, + None, + None, + ), + # Octet 14 + ( + None, + None, + None, + HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND, + None, + HCI_READ_LOCAL_SUPPORTED_FEATURES_COMMAND, + HCI_READ_LOCAL_EXTENDED_FEATURES_COMMAND, + HCI_READ_BUFFER_SIZE_COMMAND + ), + # Octet 15 + ( + None, + HCI_READ_BD_ADDR_COMMAND, + HCI_READ_FAILED_CONTACT_COUNTER_COMMAND, + HCI_RESET_FAILED_CONTACT_COUNTER_COMMAND, + HCI_READ_LINK_QUALITY_COMMAND, + HCI_READ_RSSI_COMMAND, + HCI_READ_AFH_CHANNEL_MAP_COMMAND, + HCI_READ_CLOCK_COMMAND + ), + # Octet 16 + ( + HCI_READ_LOOPBACK_MODE_COMMAND, + HCI_WRITE_LOOPBACK_MODE_COMMAND, + HCI_ENABLE_DEVICE_UNDER_TEST_MODE_COMMAND, + HCI_SETUP_SYNCHRONOUS_CONNECTION_COMMAND, + HCI_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND, + HCI_REJECT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND, + None, + None, + ), + # Octet 17 + ( + HCI_READ_EXTENDED_INQUIRY_RESPONSE_COMMAND, + HCI_WRITE_EXTENDED_INQUIRY_RESPONSE_COMMAND, + HCI_REFRESH_ENCRYPTION_KEY_COMMAND, + None, + HCI_SNIFF_SUBRATING_COMMAND, + HCI_READ_SIMPLE_PAIRING_MODE_COMMAND, + HCI_WRITE_SIMPLE_PAIRING_MODE_COMMAND, + HCI_READ_LOCAL_OOB_DATA_COMMAND + ), + # Octet 18 + ( + HCI_READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL_COMMAND, + HCI_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_COMMAND, + HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND, + HCI_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_COMMAND, + None, + None, + None, + HCI_IO_CAPABILITY_REQUEST_REPLY_COMMAND + ), + # Octet 19 + ( + HCI_USER_CONFIRMATION_REQUEST_REPLY_COMMAND, + HCI_USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY_COMMAND, + HCI_USER_PASSKEY_REQUEST_REPLY_COMMAND, + HCI_USER_PASSKEY_REQUEST_NEGATIVE_REPLY_COMMAND, + HCI_REMOTE_OOB_DATA_REQUEST_REPLY_COMMAND, + HCI_WRITE_SIMPLE_PAIRING_DEBUG_MODE_COMMAND, + HCI_ENHANCED_FLUSH_COMMAND, + HCI_REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY_COMMAND + ), + # Octet 20 + ( + None, + None, + HCI_SEND_KEYPRESS_NOTIFICATION_COMMAND, + HCI_IO_CAPABILITY_REQUEST_NEGATIVE_REPLY_COMMAND, + HCI_READ_ENCRYPTION_KEY_SIZE_COMMAND, + None, + None, + None, + ), + # Octet 21 + ( + None, + None, + None, + None, + None, + None, + None, + None, + ), + # Octet 22 + ( + None, + None, + HCI_SET_EVENT_MASK_PAGE_2_COMMAND, + None, + None, + None, + None, + None, + ), + # Octet 23 + ( + HCI_READ_FLOW_CONTROL_MODE_COMMAND, + HCI_WRITE_FLOW_CONTROL_MODE_COMMAND, + HCI_READ_DATA_BLOCK_SIZE_COMMAND, + None, + None, + None, + None, + None, + ), + # Octet 24 + ( + HCI_READ_ENHANCED_TRANSMIT_POWER_LEVEL_COMMAND, + None, + None, + None, + None, + HCI_READ_LE_HOST_SUPPORT_COMMAND, + HCI_WRITE_LE_HOST_SUPPORT_COMMAND, + None, + ), + # Octet 25 + ( + HCI_LE_SET_EVENT_MASK_COMMAND, + HCI_LE_READ_BUFFER_SIZE_COMMAND, + HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND, + None, + HCI_LE_SET_RANDOM_ADDRESS_COMMAND, + HCI_LE_SET_ADVERTISING_PARAMETERS_COMMAND, + HCI_LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER_COMMAND, + HCI_LE_SET_ADVERTISING_DATA_COMMAND, + ), + # Octet 26 + ( + HCI_LE_SET_SCAN_RESPONSE_DATA_COMMAND, + HCI_LE_SET_ADVERTISING_ENABLE_COMMAND, + HCI_LE_SET_SCAN_PARAMETERS_COMMAND, + HCI_LE_SET_SCAN_ENABLE_COMMAND, + HCI_LE_CREATE_CONNECTION_COMMAND, + HCI_LE_CREATE_CONNECTION_CANCEL_COMMAND, + HCI_LE_READ_FILTER_ACCEPT_LIST_SIZE_COMMAND, + HCI_LE_CLEAR_FILTER_ACCEPT_LIST_COMMAND + ), + # Octet 27 + ( + HCI_LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST_COMMAND, + HCI_LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST_COMMAND, + HCI_LE_CONNECTION_UPDATE_COMMAND, + HCI_LE_SET_HOST_CHANNEL_CLASSIFICATION_COMMAND, + HCI_LE_READ_CHANNEL_MAP_COMMAND, + HCI_LE_READ_REMOTE_FEATURES_COMMAND, + HCI_LE_ENCRYPT_COMMAND, + HCI_LE_RAND_COMMAND + ), + # Octet 28 + ( + HCI_LE_ENABLE_ENCRYPTION_COMMAND, + HCI_LE_LONG_TERM_KEY_REQUEST_REPLY_COMMAND, + HCI_LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY_COMMAND, + HCI_LE_READ_SUPPORTED_STATES_COMMAND, + HCI_LE_RECEIVER_TEST_COMMAND, + HCI_LE_TRANSMITTER_TEST_COMMAND, + HCI_LE_TEST_END_COMMAND, + None, + ), + # Octet 29 + ( + None, + None, + None, + HCI_ENHANCED_SETUP_SYNCHRONOUS_CONNECTION_COMMAND, + HCI_ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION_REQUEST_COMMAND, + HCI_READ_LOCAL_SUPPORTED_CODECS_COMMAND, + HCI_SET_MWS_CHANNEL_PARAMETERS_COMMAND, + HCI_SET_EXTERNAL_FRAME_CONFIGURATION_COMMAND + ), + # Octet 30 + ( + HCI_SET_MWS_SIGNALING_COMMAND, + HCI_SET_MWS_TRANSPORT_LAYER_COMMAND, + HCI_SET_MWS_SCAN_FREQUENCY_TABLE_COMMAND, + HCI_GET_MWS_TRANSPORT_LAYER_CONFIGURATION_COMMAND, + HCI_SET_MWS_PATTERN_CONFIGURATION_COMMAND, + HCI_SET_TRIGGERED_CLOCK_CAPTURE_COMMAND, + HCI_TRUNCATED_PAGE_COMMAND, + HCI_TRUNCATED_PAGE_CANCEL_COMMAND + ), + # Octet 31 + ( + HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_COMMAND, + HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE_COMMAND, + HCI_START_SYNCHRONIZATION_TRAIN_COMMAND, + HCI_RECEIVE_SYNCHRONIZATION_TRAIN_COMMAND, + HCI_SET_RESERVED_LT_ADDR_COMMAND, + HCI_DELETE_RESERVED_LT_ADDR_COMMAND, + HCI_SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_DATA_COMMAND, + HCI_READ_SYNCHRONIZATION_TRAIN_PARAMETERS_COMMAND + ), + # Octet 32 + ( + HCI_WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS_COMMAND, + HCI_REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY_COMMAND, + HCI_READ_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND, + HCI_WRITE_SECURE_CONNECTIONS_HOST_SUPPORT_COMMAND, + HCI_READ_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND, + HCI_WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT_COMMAND, + HCI_READ_LOCAL_OOB_EXTENDED_DATA_COMMAND, + HCI_WRITE_SECURE_CONNECTIONS_TEST_MODE_COMMAND + ), + # Octet 33 + ( + HCI_READ_EXTENDED_PAGE_TIMEOUT_COMMAND, + HCI_WRITE_EXTENDED_PAGE_TIMEOUT_COMMAND, + HCI_READ_EXTENDED_INQUIRY_LENGTH_COMMAND, + HCI_WRITE_EXTENDED_INQUIRY_LENGTH_COMMAND, + HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY_COMMAND, + HCI_LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY_COMMAND, + HCI_LE_SET_DATA_LENGTH_COMMAND, + HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND + ), + # Octet 34 + ( + HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND, + HCI_LE_READ_LOCAL_P_256_PUBLIC_KEY_COMMAND, + HCI_LE_GENERATE_DHKEY_COMMAND, + HCI_LE_ADD_DEVICE_TO_RESOLVING_LIST_COMMAND, + HCI_LE_REMOVE_DEVICE_FROM_RESOLVING_LIST_COMMAND, + HCI_LE_CLEAR_RESOLVING_LIST_COMMAND, + HCI_LE_READ_RESOLVING_LIST_SIZE_COMMAND, + HCI_LE_READ_PEER_RESOLVABLE_ADDRESS_COMMAND + ), + # Octet 35 + ( + HCI_LE_READ_LOCAL_RESOLVABLE_ADDRESS_COMMAND, + HCI_LE_SET_ADDRESS_RESOLUTION_ENABLE_COMMAND, + HCI_LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_COMMAND, + HCI_LE_READ_MAXIMUM_DATA_LENGTH_COMMAND, + HCI_LE_READ_PHY_COMMAND, + HCI_LE_SET_DEFAULT_PHY_COMMAND, + HCI_LE_SET_PHY_COMMAND, + HCI_LE_RECEIVER_TEST_V2_COMMAND + ), + # Octet 36 + ( + HCI_LE_TRANSMITTER_TEST_V2_COMMAND, + HCI_LE_SET_ADVERTISING_SET_RANDOM_ADDRESS_COMMAND, + HCI_LE_SET_EXTENDED_ADVERTISING_PARAMETERS_COMMAND, + HCI_LE_SET_EXTENDED_ADVERTISING_DATA_COMMAND, + HCI_LE_SET_EXTENDED_SCAN_RESPONSE_DATA_COMMAND, + HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND, + HCI_LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH_COMMAND, + HCI_LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS_COMMAND, + ), + # Octet 37 + ( + HCI_LE_REMOVE_ADVERTISING_SET_COMMAND, + HCI_LE_CLEAR_ADVERTISING_SETS_COMMAND, + HCI_LE_SET_PERIODIC_ADVERTISING_PARAMETERS_COMMAND, + HCI_LE_SET_PERIODIC_ADVERTISING_DATA_COMMAND, + HCI_LE_SET_PERIODIC_ADVERTISING_ENABLE_COMMAND, + HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND, + HCI_LE_SET_EXTENDED_SCAN_ENABLE_COMMAND, + HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND + ), + # Octet 38 + ( + HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_COMMAND, + HCI_LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL_COMMAND, + HCI_LE_PERIODIC_ADVERTISING_TERMINATE_SYNC_COMMAND, + HCI_LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST_COMMAND, + HCI_LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST_COMMAND, + HCI_LE_CLEAR_PERIODIC_ADVERTISER_LIST_COMMAND, + HCI_LE_READ_PERIODIC_ADVERTISER_LIST_SIZE_COMMAND, + HCI_LE_READ_TRANSMIT_POWER_COMMAND + ), + # Octet 39 + ( + HCI_LE_READ_RF_PATH_COMPENSATION_COMMAND, + HCI_LE_WRITE_RF_PATH_COMPENSATION_COMMAND, + HCI_LE_SET_PRIVACY_MODE_COMMAND, + HCI_LE_RECEIVER_TEST_V3_COMMAND, + HCI_LE_TRANSMITTER_TEST_V3_COMMAND, + HCI_LE_SET_CONNECTIONLESS_CTE_TRANSMIT_PARAMETERS_COMMAND, + HCI_LE_SET_CONNECTIONLESS_CTE_TRANSMIT_ENABLE_COMMAND, + HCI_LE_SET_CONNECTIONLESS_IQ_SAMPLING_ENABLE_COMMAND, + ), + # Octet 40 + ( + HCI_LE_SET_CONNECTION_CTE_RECEIVE_PARAMETERS_COMMAND, + HCI_LE_SET_CONNECTION_CTE_TRANSMIT_PARAMETERS_COMMAND, + HCI_LE_CONNECTION_CTE_REQUEST_ENABLE_COMMAND, + HCI_LE_CONNECTION_CTE_RESPONSE_ENABLE_COMMAND, + HCI_LE_READ_ANTENNA_INFORMATION_COMMAND, + HCI_LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE_COMMAND, + HCI_LE_PERIODIC_ADVERTISING_SYNC_TRANSFER_COMMAND, + HCI_LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER_COMMAND + ), + # Octet 41 + ( + HCI_LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS_COMMAND, + HCI_LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS_COMMAND, + HCI_LE_GENERATE_DHKEY_V2_COMMAND, + HCI_READ_LOCAL_SIMPLE_PAIRING_OPTIONS_COMMAND, + HCI_LE_MODIFY_SLEEP_CLOCK_ACCURACY_COMMAND, + HCI_LE_READ_BUFFER_SIZE_V2_COMMAND, + HCI_LE_READ_ISO_TX_SYNC_COMMAND, + HCI_LE_SET_CIG_PARAMETERS_COMMAND + ), + # Octet 42 + ( + HCI_LE_SET_CIG_PARAMETERS_TEST_COMMAND, + HCI_LE_CREATE_CIS_COMMAND, + HCI_LE_REMOVE_CIG_COMMAND, + HCI_LE_ACCEPT_CIS_REQUEST_COMMAND, + HCI_LE_REJECT_CIS_REQUEST_COMMAND, + HCI_LE_CREATE_BIG_COMMAND, + HCI_LE_CREATE_BIG_TEST_COMMAND, + HCI_LE_TERMINATE_BIG_COMMAND, + ), + # Octet 43 + ( + HCI_LE_BIG_CREATE_SYNC_COMMAND, + HCI_LE_BIG_TERMINATE_SYNC_COMMAND, + HCI_LE_REQUEST_PEER_SCA_COMMAND, + HCI_LE_SETUP_ISO_DATA_PATH_COMMAND, + HCI_LE_REMOVE_ISO_DATA_PATH_COMMAND, + HCI_LE_ISO_TRANSMIT_TEST_COMMAND, + HCI_LE_ISO_RECEIVE_TEST_COMMAND, + HCI_LE_ISO_READ_TEST_COUNTERS_COMMAND + ), + # Octet 44 + ( + HCI_LE_ISO_TEST_END_COMMAND, + HCI_LE_SET_HOST_FEATURE_COMMAND, + HCI_LE_READ_ISO_LINK_QUALITY_COMMAND, + HCI_LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL_COMMAND, + HCI_LE_READ_REMOTE_TRANSMIT_POWER_LEVEL_COMMAND, + HCI_LE_SET_PATH_LOSS_REPORTING_PARAMETERS_COMMAND, + HCI_LE_SET_PATH_LOSS_REPORTING_ENABLE_COMMAND, + HCI_LE_SET_TRANSMIT_POWER_REPORTING_ENABLE_COMMAND + ), + # Octet 45 + ( + HCI_LE_TRANSMITTER_TEST_V4_COMMAND, + HCI_SET_ECOSYSTEM_BASE_INTERVAL_COMMAND, + HCI_READ_LOCAL_SUPPORTED_CODECS_V2_COMMAND, + HCI_READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES_COMMAND, + HCI_READ_LOCAL_SUPPORTED_CONTROLLER_DELAY_COMMAND, + HCI_CONFIGURE_DATA_PATH_COMMAND, + HCI_LE_SET_DATA_RELATED_ADDRESS_CHANGES_COMMAND, + HCI_SET_MIN_ENCRYPTION_KEY_SIZE_COMMAND + ), + # Octet 46 + ( + HCI_LE_SET_DEFAULT_SUBRATE_COMMAND, + HCI_LE_SUBRATE_REQUEST_COMMAND, + None, + None, + None, + None, + None, + None + ) +) + +# LE Supported Features +HCI_LE_ENCRYPTION_LE_SUPPORTED_FEATURE = 0 +HCI_CONNECTION_PARAMETERS_REQUEST_PROCEDURE_LE_SUPPORTED_FEATURE = 1 +HCI_EXTENDED_REJECT_INDICATION_LE_SUPPORTED_FEATURE = 2 +HCI_PERIPHERAL_INITIATED_FEATURE_EXCHANGE_LE_SUPPORTED_FEATURE = 3 +HCI_LE_PING_LE_SUPPORTED_FEATURE = 4 +HCI_LE_DATA_PACKET_LENGTH_EXTENSION_LE_SUPPORTED_FEATURE = 5 +HCI_LL_PRIVACY_LE_SUPPORTED_FEATURE = 6 +HCI_EXTENDED_SCANNER_FILTER_POLICIES_LE_SUPPORTED_FEATURE = 7 +HCI_LE_2M_PHY_LE_SUPPORTED_FEATURE = 8 +HCI_STABLE_MODULATION_INDEX_TRANSMITTER_LE_SUPPORTED_FEATURE = 9 +HCI_STABLE_MODULATION_INDEX_RECEIVER_LE_SUPPORTED_FEATURE = 10 +HCI_LE_CODED_PHY_LE_SUPPORTED_FEATURE = 11 +HCI_LE_EXTENDED_ADVERTISING_LE_SUPPORTED_FEATURE = 12 +HCI_LE_PERIODIC_ADVERTISING_LE_SUPPORTED_FEATURE = 13 +HCI_CHANNEL_SELECTION_ALGORITHM_2_LE_SUPPORTED_FEATURE = 14 +HCI_LE_POWER_CLASS_1_LE_SUPPORTED_FEATURE = 15 +HCI_MINIMUM_NUMBER_OF_USED_CHANNELS_PROCEDURE_LE_SUPPORTED_FEATURE = 16 +HCI_CONNECTION_CTE_REQUEST_LE_SUPPORTED_FEATURE = 17 +HCI_CONNECTION_CTE_RESPONSE_LE_SUPPORTED_FEATURE = 18 +HCI_CONNECTIONLESS_CTE_TRANSMITTER_LE_SUPPORTED_FEATURE = 19 +HCI_CONNECTIONLESS_CTR_RECEIVER_LE_SUPPORTED_FEATURE = 20 +HCI_ANTENNA_SWITCHING_DURING_CTE_TRANSMISSION_LE_SUPPORTED_FEATURE = 21 +HCI_ANTENNA_SWITCHING_DURING_CTE_RECEPTION_LE_SUPPORTED_FEATURE = 22 +HCI_RECEIVING_CONSTANT_TONE_EXTENSIONS_LE_SUPPORTED_FEATURE = 23 +HCI_PERIODIC_ADVERTISING_SYNC_TRANSFER_SENDER_LE_SUPPORTED_FEATURE = 24 +HCI_PERIODIC_ADVERTISING_SYNC_TRANSFER_RECIPIENT_LE_SUPPORTED_FEATURE = 25 +HCI_SLEEP_CLOCK_ACCURACY_UPDATES_LE_SUPPORTED_FEATURE = 26 +HCI_REMOTE_PUBLIC_KEY_VALIDATION_LE_SUPPORTED_FEATURE = 27 +HCI_CONNECTED_ISOCHRONOUS_STREAM_CENTRAL_LE_SUPPORTED_FEATURE = 28 +HCI_CONNECTED_ISOCHRONOUS_STREAM_PERIPHERAL_LE_SUPPORTED_FEATURE = 29 +HCI_ISOCHRONOUS_BROADCASTER_LE_SUPPORTED_FEATURE = 30 +HCI_SYNCHRONIZED_RECEIVER_LE_SUPPORTED_FEATURE = 31 +HCI_CONNECTED_ISOCHRONOUS_STREAM_LE_SUPPORTED_FEATURE = 32 +HCI_LE_POWER_CONTROL_REQUEST_LE_SUPPORTED_FEATURE = 33 +HCI_LE_POWER_CONTROL_REQUEST_DUP_LE_SUPPORTED_FEATURE = 34 +HCI_LE_PATH_LOSS_MONITORING_LE_SUPPORTED_FEATURE = 35 +HCI_PERIODIC_ADVERTISING_ADI_SUPPORT_LE_SUPPORTED_FEATURE = 36 +HCI_CONNECTION_SUBRATING_LE_SUPPORTED_FEATURE = 37 +HCI_CONNECTION_SUBRATING_HOST_SUPPORT_LE_SUPPORTED_FEATURE = 38 +HCI_CHANNEL_CLASSIFICATION_LE_SUPPORTED_FEATURE = 39 + +HCI_LE_SUPPORTED_FEATURES_NAMES = { + flag: feature_name for (feature_name, flag) in globals().items() + if feature_name.startswith('HCI_') and feature_name.endswith('_LE_SUPPORTED_FEATURE') +} + +# fmt: on +# pylint: enable=line-too-long +# pylint: disable=invalid-name + # ----------------------------------------------------------------------------- +# pylint: disable-next=unnecessary-lambda STATUS_SPEC = {'size': 1, 'mapper': lambda x: HCI_Constant.status_name(x)} @@ -768,8 +1410,7 @@ class HCI_Constant: @staticmethod def authentication_requirements_name(authentication_requirements): return HCI_AUTHENTICATION_REQUIREMENTS_NAMES.get( - authentication_requirements, - f'0x{authentication_requirements:02X}' + authentication_requirements, f'0x{authentication_requirements:02X}' ) @staticmethod @@ -780,7 +1421,21 @@ class HCI_Constant: # ----------------------------------------------------------------------------- class HCI_Error(ProtocolError): def __init__(self, error_code): - super().__init__(error_code, 'hci', HCI_Constant.error_name(error_code)) + super().__init__( + error_code, + error_namespace='hci', + error_name=HCI_Constant.error_name(error_code), + ) + + +# ----------------------------------------------------------------------------- +class HCI_StatusError(ProtocolError): + def __init__(self, response): + super().__init__( + response.status, + error_namespace=HCI_Command.command_name(response.command_opcode), + error_name=HCI_Constant.status_name(response.status), + ) # ----------------------------------------------------------------------------- @@ -788,25 +1443,25 @@ class HCI_Error(ProtocolError): # ----------------------------------------------------------------------------- class HCI_Object: @staticmethod - def init_from_fields(object, fields, values): - if type(values) is dict: + def init_from_fields(hci_object, fields, values): + if isinstance(values, dict): for field_name, _ in fields: - setattr(object, field_name, values[field_name]) + setattr(hci_object, field_name, values[field_name]) else: for field_name, field_value in zip(fields, values): - setattr(object, field_name, field_value) + setattr(hci_object, field_name, field_value) @staticmethod - def init_from_bytes(object, data, offset, fields): + def init_from_bytes(hci_object, data, offset, fields): parsed = HCI_Object.dict_from_bytes(data, offset, fields) - HCI_Object.init_from_fields(object, parsed.keys(), parsed.values()) + HCI_Object.init_from_fields(hci_object, parsed.keys(), parsed.values()) @staticmethod def dict_from_bytes(data, offset, fields): result = collections.OrderedDict() for (field_name, field_type) in fields: - # The field_type may be a dictionnary with a mapper, parser, and/or size - if type(field_type) is dict: + # The field_type may be a dictionary with a mapper, parser, and/or size + if isinstance(field_type, dict): if 'size' in field_type: field_type = field_type['size'] elif 'parser' in field_type: @@ -836,10 +1491,10 @@ class HCI_Object: elif field_type == -2: # 16-bit signed field_value = struct.unpack_from('<h', data, offset)[0] - offset += 1 + offset += 2 elif field_type == 3: # 24-bit unsigned - padded = data[offset:offset + 3] + bytes([0]) + padded = data[offset : offset + 3] + bytes([0]) field_value = struct.unpack('<I', padded)[0] offset += 3 elif field_type == 4: @@ -850,9 +1505,9 @@ class HCI_Object: # 32-bit unsigned big-endian field_value = struct.unpack_from('>I', data, offset)[0] offset += 4 - elif type(field_type) is int and field_type > 4 and field_type <= 256: + elif isinstance(field_type, int) and 4 < field_type <= 256: # Byte array (from 5 up to 256 bytes) - field_value = data[offset:offset + field_type] + field_value = data[offset : offset + field_type] offset += field_type elif callable(field_type): offset, field_value = field_type(data, offset) @@ -864,19 +1519,20 @@ class HCI_Object: return result @staticmethod - def dict_to_bytes(object, fields): + def dict_to_bytes(hci_object, fields): result = bytearray() for (field_name, field_type) in fields: - # The field_type may be a dictionnary with a mapper, parser, serializer, and/or size + # The field_type may be a dictionary with a mapper, parser, serializer, + # and/or size serializer = None - if type(field_type) is dict: + if isinstance(field_type, dict): if 'serializer' in field_type: serializer = field_type['serializer'] if 'size' in field_type: field_type = field_type['size'] # Serialize the field - field_value = object[field_name] + field_value = hci_object[field_name] if serializer: field_bytes = serializer(field_value) elif field_type == 1: @@ -904,31 +1560,35 @@ class HCI_Object: # 32-bit unsigned big-endian field_bytes = struct.pack('>I', field_value) elif field_type == '*': - if type(field_value) is int: - if field_value >= 0 and field_value <= 255: + if isinstance(field_value, int): + if 0 <= field_value <= 255: field_bytes = bytes([field_value]) else: raise ValueError('value too large for *-typed field') else: field_bytes = bytes(field_value) - elif type(field_value) is bytes or type(field_value) is bytearray or hasattr(field_value, 'to_bytes'): + elif isinstance(field_value, (bytes, bytearray)) or hasattr( + field_value, 'to_bytes' + ): field_bytes = bytes(field_value) - if type(field_type) is int and field_type > 4 and field_type <= 256: + if isinstance(field_type, int) and 4 < field_type <= 256: # Truncate or Pad with zeros if the field is too long or too short if len(field_bytes) < field_type: field_bytes += bytes(field_type - len(field_bytes)) elif len(field_bytes) > field_type: field_bytes = field_bytes[:field_type] else: - raise ValueError(f"don't know how to serialize type {type(field_value)}") + raise ValueError( + f"don't know how to serialize type {type(field_value)}" + ) result += field_bytes return bytes(result) - @staticmethod - def from_bytes(data, offset, fields): - return HCI_Object(fields, **HCI_Object.dict_from_bytes(data, offset, fields)) + @classmethod + def from_bytes(cls, data, offset, fields): + return cls(fields, **cls.dict_from_bytes(data, offset, fields)) def to_bytes(self): return HCI_Object.dict_to_bytes(self.__dict__, self.fields) @@ -936,53 +1596,61 @@ class HCI_Object: @staticmethod def parse_length_prefixed_bytes(data, offset): length = data[offset] - return offset + 1 + length, data[offset + 1:offset + 1 + length] + return offset + 1 + length, data[offset + 1 : offset + 1 + length] @staticmethod def serialize_length_prefixed_bytes(data, padded_size=0): prefixed_size = 1 + len(data) - padding = bytes(padded_size - prefixed_size) if prefixed_size < padded_size else b'' + padding = ( + bytes(padded_size - prefixed_size) if prefixed_size < padded_size else b'' + ) return bytes([len(data)]) + data + padding @staticmethod def format_field_value(value, indentation): - if type(value) is bytes: + if isinstance(value, bytes): return value.hex() - elif isinstance(value, HCI_Object): + + if isinstance(value, HCI_Object): return '\n' + value.to_string(indentation) - else: - return str(value) + + return str(value) @staticmethod - def format_fields(object, keys, indentation='', value_mappers={}): + def format_fields(hci_object, keys, indentation='', value_mappers=None): if not keys: return '' # Measure the widest field name - max_field_name_length = max([len(key[0] if type(key) is tuple else key) for key in keys]) + max_field_name_length = max( + (len(key[0] if isinstance(key, tuple) else key) for key in keys) + ) # Build array of formatted key:value pairs fields = [] for key in keys: value_mapper = None - if type(key) is tuple: + if isinstance(key, tuple): # The key has an associated specifier key, specifier = key # Get the value mapper from the specifier - if type(specifier) is dict: + if isinstance(specifier, dict): value_mapper = specifier.get('mapper') # Get the value for the field - value = object[key] + value = hci_object[key] # Map the value if needed - value_mapper = value_mappers.get(key, value_mapper) + if value_mappers: + value_mapper = value_mappers.get(key, value_mapper) if value_mapper is not None: value = value_mapper(value) # Get the string representation of the value - value_str = HCI_Object.format_field_value(value, indentation = indentation + ' ') + value_str = HCI_Object.format_field_value( + value, indentation=indentation + ' ' + ) # Add the field to the formatted result key_str = color(f'{key + ":":{1 + max_field_name_length}}', 'cyan') @@ -997,8 +1665,10 @@ class HCI_Object: self.fields = fields self.init_from_fields(self, fields, kwargs) - def to_string(self, indentation='', value_mappers={}): - return HCI_Object.format_fields(self.__dict__, self.fields, indentation, value_mappers) + def to_string(self, indentation='', value_mappers=None): + return HCI_Object.format_fields( + self.__dict__, self.fields, indentation, value_mappers + ) def __str__(self): return self.to_string() @@ -1014,18 +1684,24 @@ class Address: address[0] is the LSB of the address, address[5] is the MSB. ''' - PUBLIC_DEVICE_ADDRESS = 0x00 - RANDOM_DEVICE_ADDRESS = 0x01 + PUBLIC_DEVICE_ADDRESS = 0x00 + RANDOM_DEVICE_ADDRESS = 0x01 PUBLIC_IDENTITY_ADDRESS = 0x02 RANDOM_IDENTITY_ADDRESS = 0x03 ADDRESS_TYPE_NAMES = { - PUBLIC_DEVICE_ADDRESS: 'PUBLIC_DEVICE_ADDRESS', - RANDOM_DEVICE_ADDRESS: 'RANDOM_DEVICE_ADDRESS', + PUBLIC_DEVICE_ADDRESS: 'PUBLIC_DEVICE_ADDRESS', + RANDOM_DEVICE_ADDRESS: 'RANDOM_DEVICE_ADDRESS', PUBLIC_IDENTITY_ADDRESS: 'PUBLIC_IDENTITY_ADDRESS', - RANDOM_IDENTITY_ADDRESS: 'RANDOM_IDENTITY_ADDRESS' + RANDOM_IDENTITY_ADDRESS: 'RANDOM_IDENTITY_ADDRESS', } + # Type declarations + NIL: Address + ANY: Address + ANY_RANDOM: Address + + # pylint: disable-next=unnecessary-lambda ADDRESS_TYPE_SPEC = {'size': 1, 'mapper': lambda x: Address.address_type_name(x)} @staticmethod @@ -1033,28 +1709,41 @@ class Address: return name_or_number(Address.ADDRESS_TYPE_NAMES, address_type) @staticmethod + def from_string_for_transport(string, transport): + if transport == BT_BR_EDR_TRANSPORT: + address_type = Address.PUBLIC_DEVICE_ADDRESS + else: + address_type = Address.RANDOM_DEVICE_ADDRESS + return Address(string, address_type) + + @staticmethod def parse_address(data, offset): - # Fix the type to a default value. This is used for parsing type-less Classic addresses - return Address.parse_address_with_type(data, offset, Address.PUBLIC_DEVICE_ADDRESS) + # Fix the type to a default value. This is used for parsing type-less Classic + # addresses + return Address.parse_address_with_type( + data, offset, Address.PUBLIC_DEVICE_ADDRESS + ) @staticmethod def parse_address_with_type(data, offset, address_type): - return offset + 6, Address(data[offset:offset + 6], address_type) + return offset + 6, Address(data[offset : offset + 6], address_type) @staticmethod def parse_address_preceded_by_type(data, offset): address_type = data[offset - 1] return Address.parse_address_with_type(data, offset, address_type) - def __init__(self, address, address_type = RANDOM_DEVICE_ADDRESS): + def __init__( + self, address: Union[bytes, str], address_type: int = RANDOM_DEVICE_ADDRESS + ): ''' Initialize an instance. `address` may be a byte array in little-endian format, or a hex string in big-endian format (with optional ':' separators between the bytes). - If the address is a string suffixed with '/P', `address_type` is ignored and the type - is set to PUBLIC_DEVICE_ADDRESS. + If the address is a string suffixed with '/P', `address_type` is ignored and + the type is set to PUBLIC_DEVICE_ADDRESS. ''' - if type(address) is bytes: + if isinstance(address, bytes): self.address_bytes = address else: # Check if there's a '/P' type specifier @@ -1072,9 +1761,15 @@ class Address: self.address_type = address_type + def clone(self): + return Address(self.address_bytes, self.address_type) + @property def is_public(self): - return self.address_type == self.PUBLIC_DEVICE_ADDRESS or self.address_type == self.PUBLIC_IDENTITY_ADDRESS + return self.address_type in ( + self.PUBLIC_DEVICE_ADDRESS, + self.PUBLIC_IDENTITY_ADDRESS, + ) @property def is_random(self): @@ -1082,11 +1777,16 @@ class Address: @property def is_resolved(self): - return self.address_type == self.PUBLIC_IDENTITY_ADDRESS or self.address_type == self.RANDOM_IDENTITY_ADDRESS + return self.address_type in ( + self.PUBLIC_IDENTITY_ADDRESS, + self.RANDOM_IDENTITY_ADDRESS, + ) @property def is_resolvable(self): - return self.address_type == self.RANDOM_DEVICE_ADDRESS and (self.address_bytes[5] >> 6 == 1) + return self.address_type == self.RANDOM_DEVICE_ADDRESS and ( + self.address_bytes[5] >> 6 == 1 + ) @property def is_static(self): @@ -1102,13 +1802,46 @@ class Address: return hash(self.address_bytes) def __eq__(self, other): - return self.address_bytes == other.address_bytes and self.is_public == other.is_public + return ( + self.address_bytes == other.address_bytes + and self.is_public == other.is_public + ) def __str__(self): ''' String representation of the address, MSB first ''' - return ':'.join([f'{x:02X}' for x in reversed(self.address_bytes)]) + result = ':'.join([f'{x:02X}' for x in reversed(self.address_bytes)]) + if not self.is_public: + return result + return result + '/P' + + +# Predefined address values +Address.NIL = Address(b"\xff\xff\xff\xff\xff\xff", Address.PUBLIC_DEVICE_ADDRESS) +Address.ANY = Address(b"\x00\x00\x00\x00\x00\x00", Address.PUBLIC_DEVICE_ADDRESS) +Address.ANY_RANDOM = Address(b"\x00\x00\x00\x00\x00\x00", Address.RANDOM_DEVICE_ADDRESS) + +# ----------------------------------------------------------------------------- +class OwnAddressType: + PUBLIC = 0 + RANDOM = 1 + RESOLVABLE_OR_PUBLIC = 2 + RESOLVABLE_OR_RANDOM = 3 + + TYPE_NAMES = { + PUBLIC: 'PUBLIC', + RANDOM: 'RANDOM', + RESOLVABLE_OR_PUBLIC: 'RESOLVABLE_OR_PUBLIC', + RESOLVABLE_OR_RANDOM: 'RESOLVABLE_OR_RANDOM', + } + + @staticmethod + def type_name(type_id): + return name_or_number(OwnAddressType.TYPE_NAMES, type_id) + + # pylint: disable-next=unnecessary-lambda + TYPE_SPEC = {'size': 1, 'mapper': lambda x: OwnAddressType.type_name(x)} # ----------------------------------------------------------------------------- @@ -1117,21 +1850,29 @@ class HCI_Packet: Abstract Base class for HCI packets ''' + hci_packet_type: int + @staticmethod def from_bytes(packet): packet_type = packet[0] + if packet_type == HCI_COMMAND_PACKET: return HCI_Command.from_bytes(packet) - elif packet_type == HCI_ACL_DATA_PACKET: + + if packet_type == HCI_ACL_DATA_PACKET: return HCI_AclDataPacket.from_bytes(packet) - elif packet_type == HCI_EVENT_PACKET: + + if packet_type == HCI_EVENT_PACKET: return HCI_Event.from_bytes(packet) - else: - return HCI_CustomPacket(packet) + + return HCI_CustomPacket(packet) def __init__(self, name): self.name = name + def __bytes__(self) -> bytes: + raise NotImplementedError + def __repr__(self) -> str: return self.name @@ -1141,7 +1882,10 @@ class HCI_CustomPacket(HCI_Packet): def __init__(self, payload): super().__init__('HCI_CUSTOM_PACKET') self.hci_packet_type = payload[0] - self.payload = payload + self.payload = payload + + def __bytes__(self) -> bytes: + return self.payload # ----------------------------------------------------------------------------- @@ -1149,11 +1893,12 @@ class HCI_Command(HCI_Packet): ''' See Bluetooth spec @ Vol 2, Part E - 5.4.1 HCI Command Packet ''' + hci_packet_type = HCI_COMMAND_PACKET - command_classes = {} + command_classes: Dict[int, Type[HCI_Command]] = {} @staticmethod - def command(fields=[], return_parameters_fields=[]): + def command(fields=(), return_parameters_fields=()): ''' Decorator used to declare and register subclasses ''' @@ -1162,14 +1907,17 @@ class HCI_Command(HCI_Packet): cls.name = cls.__name__.upper() cls.op_code = key_with_value(HCI_COMMAND_NAMES, cls.name) if cls.op_code is None: - raise KeyError('command not found in HCI_COMMAND_NAMES') + raise KeyError(f'command {cls.name} not found in HCI_COMMAND_NAMES') cls.fields = fields cls.return_parameters_fields = return_parameters_fields # Patch the __init__ method to fix the op_code - def init(self, parameters=None, **kwargs): - return HCI_Command.__init__(self, cls.op_code, parameters, **kwargs) - cls.__init__ = init + if fields is not None: + + def init(self, parameters=None, **kwargs): + return HCI_Command.__init__(self, cls.op_code, parameters, **kwargs) + + cls.__init__ = init # Register a factory for this class HCI_Command.command_classes[cls.op_code] = cls @@ -1192,11 +1940,13 @@ class HCI_Command(HCI_Packet): return HCI_Command(op_code, parameters) # Create a new instance - self = cls.__new__(cls) - HCI_Command.__init__(self, op_code, parameters) - if fields := getattr(self, 'fields', None): + if (fields := getattr(cls, 'fields', None)) is not None: + self = cls.__new__(cls) + HCI_Command.__init__(self, op_code, parameters) HCI_Object.init_from_bytes(self, parameters, 0, fields) - return self + return self + + return cls.from_parameters(parameters) @staticmethod def command_name(op_code): @@ -1215,12 +1965,15 @@ class HCI_Command(HCI_Packet): HCI_Object.init_from_fields(self, fields, kwargs) if parameters is None: parameters = HCI_Object.dict_to_bytes(kwargs, fields) - self.op_code = op_code + self.op_code = op_code self.parameters = parameters def to_bytes(self): parameters = b'' if self.parameters is None else self.parameters - return struct.pack('<BHB', HCI_COMMAND_PACKET, self.op_code, len(parameters)) + parameters + return ( + struct.pack('<BHB', HCI_COMMAND_PACKET, self.op_code, len(parameters)) + + parameters + ) def __bytes__(self): return self.to_bytes() @@ -1236,11 +1989,13 @@ class HCI_Command(HCI_Packet): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('lap', {'size': 3, 'mapper': HCI_Constant.inquiry_lap_name}), - ('inquiry_length', 1), - ('num_responses', 1) -]) +@HCI_Command.command( + [ + ('lap', {'size': 3, 'mapper': HCI_Constant.inquiry_lap_name}), + ('inquiry_length', 1), + ('num_responses', 1), + ] +) class HCI_Inquiry_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.1 Inquiry Command @@ -1256,14 +2011,16 @@ class HCI_Inquiry_Cancel_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('bd_addr', Address.parse_address), - ('packet_type', 2), - ('page_scan_repetition_mode', 1), - ('reserved', 1), - ('clock_offset', 2), - ('allow_role_switch', 1) -]) +@HCI_Command.command( + [ + ('bd_addr', Address.parse_address), + ('packet_type', 2), + ('page_scan_repetition_mode', 1), + ('reserved', 1), + ('clock_offset', 2), + ('allow_role_switch', 1), + ] +) class HCI_Create_Connection_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.5 Create Connection Command @@ -1271,10 +2028,12 @@ class HCI_Create_Connection_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}) -]) +@HCI_Command.command( + [ + ('connection_handle', 2), + ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}), + ] +) class HCI_Disconnect_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.6 Disconnect Command @@ -1282,10 +2041,21 @@ class HCI_Disconnect_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('bd_addr', Address.parse_address), - ('role', 1) -]) +@HCI_Command.command( + fields=[('bd_addr', Address.parse_address)], + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ], +) +class HCI_Create_Connection_Cancel_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.1.7 Create Connection Cancel Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command([('bd_addr', Address.parse_address), ('role', 1)]) class HCI_Accept_Connection_Request_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.8 Accept Connection Request Command @@ -1293,10 +2063,20 @@ class HCI_Accept_Connection_Request_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('bd_addr', Address.parse_address), - ('link_key', 16) -]) +@HCI_Command.command( + [ + ('bd_addr', Address.parse_address), + ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}), + ] +) +class HCI_Reject_Connection_Request_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.1.9 Reject Connection Request Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command([('bd_addr', Address.parse_address), ('link_key', 16)]) class HCI_Link_Key_Request_Reply_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.10 Link Key Request Reply Command @@ -1305,13 +2085,11 @@ class HCI_Link_Key_Request_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ - ('bd_addr', Address.parse_address) - ], + fields=[('bd_addr', Address.parse_address)], return_parameters_fields=[ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address) - ] + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ], ) class HCI_Link_Key_Request_Negative_Reply_Command(HCI_Command): ''' @@ -1322,12 +2100,28 @@ class HCI_Link_Key_Request_Negative_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( fields=[ - ('bd_addr', Address.parse_address) + ('bd_addr', Address.parse_address), + ('pin_code_length', 1), + ('pin_code', 16), ], return_parameters_fields=[ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address) - ] + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ], +) +class HCI_PIN_Code_Request_Reply_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.1.12 PIN Code Request Reply Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + fields=[('bd_addr', Address.parse_address)], + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ], ) class HCI_PIN_Code_Request_Negative_Reply_Command(HCI_Command): ''' @@ -1336,10 +2130,7 @@ class HCI_PIN_Code_Request_Negative_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('packet_type', 2) -]) +@HCI_Command.command([('connection_handle', 2), ('packet_type', 2)]) class HCI_Change_Connection_Packet_Type_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.14 Change Connection Packet Type Command @@ -1347,9 +2138,7 @@ class HCI_Change_Connection_Packet_Type_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2) -]) +@HCI_Command.command([('connection_handle', 2)]) class HCI_Authentication_Requested_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.15 Authentication Requested Command @@ -1357,10 +2146,7 @@ class HCI_Authentication_Requested_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('encryption_enable', 1) -]) +@HCI_Command.command([('connection_handle', 2), ('encryption_enable', 1)]) class HCI_Set_Connection_Encryption_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.16 Set Connection Encryption Command @@ -1368,25 +2154,26 @@ class HCI_Set_Connection_Encryption_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('bd_addr', Address.parse_address), - ('page_scan_repetition_mode', 1), - ('reserved', 1), - ('clock_offset', 2) -]) +@HCI_Command.command( + [ + ('bd_addr', Address.parse_address), + ('page_scan_repetition_mode', 1), + ('reserved', 1), + ('clock_offset', 2), + ] +) class HCI_Remote_Name_Request_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.19 Remote Name Request Command ''' + R0 = 0x00 R1 = 0x01 R2 = 0x02 # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2) -]) +@HCI_Command.command([('connection_handle', 2)]) class HCI_Read_Remote_Supported_Features_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.21 Read Remote Supported Features Command @@ -1394,10 +2181,7 @@ class HCI_Read_Remote_Supported_Features_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('page_number', 1) -]) +@HCI_Command.command([('connection_handle', 2), ('page_number', 1)]) class HCI_Read_Remote_Extended_Features_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.22 Read Remote Extended Features Command @@ -1405,9 +2189,7 @@ class HCI_Read_Remote_Extended_Features_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2) -]) +@HCI_Command.command([('connection_handle', 2)]) class HCI_Read_Remote_Version_Information_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.23 Read Remote Version Information Command @@ -1415,9 +2197,7 @@ class HCI_Read_Remote_Version_Information_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2) -]) +@HCI_Command.command([('connection_handle', 2)]) class HCI_Read_Clock_Offset_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.23 Read Clock Offset Command @@ -1427,15 +2207,18 @@ class HCI_Read_Clock_Offset_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( fields=[ - ('bd_addr', Address.parse_address), - ('io_capability', {'size': 1, 'mapper': HCI_Constant.io_capability_name}), - ('oob_data_present', 1), - ('authentication_requirements', {'size': 1, 'mapper': HCI_Constant.authentication_requirements_name}) + ('bd_addr', Address.parse_address), + ('io_capability', {'size': 1, 'mapper': HCI_Constant.io_capability_name}), + ('oob_data_present', 1), + ( + 'authentication_requirements', + {'size': 1, 'mapper': HCI_Constant.authentication_requirements_name}, + ), ], return_parameters_fields=[ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address) - ] + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ], ) class HCI_IO_Capability_Request_Reply_Command(HCI_Command): ''' @@ -1445,13 +2228,11 @@ class HCI_IO_Capability_Request_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ - ('bd_addr', Address.parse_address) - ], + fields=[('bd_addr', Address.parse_address)], return_parameters_fields=[ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address) - ] + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ], ) class HCI_User_Confirmation_Request_Reply_Command(HCI_Command): ''' @@ -1461,13 +2242,11 @@ class HCI_User_Confirmation_Request_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ - ('bd_addr', Address.parse_address) - ], + fields=[('bd_addr', Address.parse_address)], return_parameters_fields=[ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address) - ] + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ], ) class HCI_User_Confirmation_Request_Negative_Reply_Command(HCI_Command): ''' @@ -1477,14 +2256,11 @@ class HCI_User_Confirmation_Request_Negative_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ + fields=[('bd_addr', Address.parse_address), ('numeric_value', 4)], + return_parameters_fields=[ + ('status', STATUS_SPEC), ('bd_addr', Address.parse_address), - ('numeric_value', 4) ], - return_parameters_fields=[ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address) - ] ) class HCI_User_Passkey_Request_Reply_Command(HCI_Command): ''' @@ -1494,13 +2270,11 @@ class HCI_User_Passkey_Request_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ - ('bd_addr', Address.parse_address) - ], + fields=[('bd_addr', Address.parse_address)], return_parameters_fields=[ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address) - ] + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ], ) class HCI_User_Passkey_Request_Negative_Reply_Command(HCI_Command): ''' @@ -1509,32 +2283,34 @@ class HCI_User_Passkey_Request_Negative_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('transmit_bandwidth', 4), - ('receive_bandwidth', 4), - ('transmit_coding_format', 5), - ('receive_coding_format', 5), - ('transmit_codec_frame_size', 2), - ('receive_codec_frame_size', 2), - ('input_bandwidth', 4), - ('output_bandwidth', 4), - ('input_coding_format', 5), - ('output_coding_format', 5), - ('input_coded_data_size', 2), - ('output_coded_data_size', 2), - ('input_pcm_data_format', 1), - ('output_pcm_data_format', 1), - ('input_pcm_sample_payload_msb_position', 1), - ('output_pcm_sample_payload_msb_position', 1), - ('input_data_path', 1), - ('output_data_path', 1), - ('input_transport_unit_size', 1), - ('output_transport_unit_size', 1), - ('max_latency', 2), - ('packet_type', 2), - ('retransmission_effort', 1) -]) +@HCI_Command.command( + [ + ('connection_handle', 2), + ('transmit_bandwidth', 4), + ('receive_bandwidth', 4), + ('transmit_coding_format', 5), + ('receive_coding_format', 5), + ('transmit_codec_frame_size', 2), + ('receive_codec_frame_size', 2), + ('input_bandwidth', 4), + ('output_bandwidth', 4), + ('input_coding_format', 5), + ('output_coding_format', 5), + ('input_coded_data_size', 2), + ('output_coded_data_size', 2), + ('input_pcm_data_format', 1), + ('output_pcm_data_format', 1), + ('input_pcm_sample_payload_msb_position', 1), + ('output_pcm_sample_payload_msb_position', 1), + ('input_data_path', 1), + ('output_data_path', 1), + ('input_transport_unit_size', 1), + ('output_transport_unit_size', 1), + ('max_latency', 2), + ('packet_type', 2), + ('retransmission_effort', 1), + ] +) class HCI_Enhanced_Setup_Synchronous_Connection_Command(HCI_Command): ''' See Bluetooth spec @ 7.1.45 Enhanced Setup Synchronous Connection Command @@ -1542,13 +2318,15 @@ class HCI_Enhanced_Setup_Synchronous_Connection_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('sniff_max_interval', 2), - ('sniff_min_interval', 2), - ('sniff_attempt', 2), - ('sniff_timeout', 2) -]) +@HCI_Command.command( + [ + ('connection_handle', 2), + ('sniff_max_interval', 2), + ('sniff_min_interval', 2), + ('sniff_attempt', 2), + ('sniff_timeout', 2), + ] +) class HCI_Sniff_Mode_Command(HCI_Command): ''' See Bluetooth spec @ 7.2.2 Sniff Mode Command @@ -1556,9 +2334,7 @@ class HCI_Sniff_Mode_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2) -]) +@HCI_Command.command([('connection_handle', 2)]) class HCI_Exit_Sniff_Mode_Command(HCI_Command): ''' See Bluetooth spec @ 7.2.3 Exit Sniff Mode Command @@ -1566,10 +2342,12 @@ class HCI_Exit_Sniff_Mode_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('bd_addr', Address.parse_address), - ('role', {'size': 1, 'mapper': HCI_Constant.role_name}) -]) +@HCI_Command.command( + [ + ('bd_addr', Address.parse_address), + ('role', {'size': 1, 'mapper': HCI_Constant.role_name}), + ] +) class HCI_Switch_Role_Command(HCI_Command): ''' See Bluetooth spec @ 7.2.8 Switch Role Command @@ -1577,10 +2355,7 @@ class HCI_Switch_Role_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('link_policy_settings', 2) -]) +@HCI_Command.command([('connection_handle', 2), ('link_policy_settings', 2)]) class HCI_Write_Link_Policy_Settings_Command(HCI_Command): ''' See Bluetooth spec @ 7.2.10 Write Link Policy Settings Command @@ -1588,9 +2363,7 @@ class HCI_Write_Link_Policy_Settings_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('default_link_policy_settings', 2) -]) +@HCI_Command.command([('default_link_policy_settings', 2)]) class HCI_Write_Default_Link_Policy_Settings_Command(HCI_Command): ''' See Bluetooth spec @ 7.2.12 Write Default Link Policy Settings Command @@ -1598,12 +2371,14 @@ class HCI_Write_Default_Link_Policy_Settings_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('maximum_latency', 2), - ('minimum_remote_timeout', 2), - ('minimum_local_timeout', 2) -]) +@HCI_Command.command( + [ + ('connection_handle', 2), + ('maximum_latency', 2), + ('minimum_remote_timeout', 2), + ('minimum_local_timeout', 2), + ] +) class HCI_Sniff_Subrating_Command(HCI_Command): ''' See Bluetooth spec @ 7.2.14 Sniff Subrating Command @@ -1611,9 +2386,7 @@ class HCI_Sniff_Subrating_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('event_mask', 8) -]) +@HCI_Command.command([('event_mask', 8)]) class HCI_Set_Event_Mask_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.1 Set Event Mask Command @@ -1629,10 +2402,12 @@ class HCI_Reset_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('filter_type', 1), - ('filter_condition', '*'), -]) +@HCI_Command.command( + [ + ('filter_type', 1), + ('filter_condition', '*'), + ] +) class HCI_Set_Event_Filter_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.3 Set Event Filter Command @@ -1641,15 +2416,12 @@ class HCI_Set_Event_Filter_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ - ('bd_addr', Address.parse_address), - ('read_all_flag', 1) - ], + fields=[('bd_addr', Address.parse_address), ('read_all_flag', 1)], return_parameters_fields=[ - ('status', STATUS_SPEC), - ('max_num_keys', 2), - ('num_keys_read', 2) - ] + ('status', STATUS_SPEC), + ('max_num_keys', 2), + ('num_keys_read', 2), + ], ) class HCI_Read_Stored_Link_Key_Command(HCI_Command): ''' @@ -1659,14 +2431,8 @@ class HCI_Read_Stored_Link_Key_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ - ('bd_addr', Address.parse_address), - ('delete_all_flag', 1) - ], - return_parameters_fields=[ - ('status', STATUS_SPEC), - ('num_keys_deleted', 2) - ] + fields=[('bd_addr', Address.parse_address), ('delete_all_flag', 1)], + return_parameters_fields=[('status', STATUS_SPEC), ('num_keys_deleted', 2)], ) class HCI_Delete_Stored_Link_Key_Command(HCI_Command): ''' @@ -1675,9 +2441,9 @@ class HCI_Delete_Stored_Link_Key_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('local_name', {'size': 248, 'mapper': map_null_terminated_utf8_string}) -]) +@HCI_Command.command( + [('local_name', {'size': 248, 'mapper': map_null_terminated_utf8_string})] +) class HCI_Write_Local_Name_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.11 Write Local Name Command @@ -1685,10 +2451,12 @@ class HCI_Write_Local_Name_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('local_name', {'size': 248, 'mapper': map_null_terminated_utf8_string}) -]) +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('local_name', {'size': 248, 'mapper': map_null_terminated_utf8_string}), + ] +) class HCI_Read_Local_Name_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.12 Read Local Name Command @@ -1696,9 +2464,7 @@ class HCI_Read_Local_Name_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('conn_accept_timeout', 2) -]) +@HCI_Command.command([('connection_accept_timeout', 2)]) class HCI_Write_Connection_Accept_Timeout_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.14 Write Connection Accept Timeout Command @@ -1706,9 +2472,7 @@ class HCI_Write_Connection_Accept_Timeout_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('page_timeout', 2) -]) +@HCI_Command.command([('page_timeout', 2)]) class HCI_Write_Page_Timeout_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.16 Write Page Timeout Command @@ -1716,9 +2480,7 @@ class HCI_Write_Page_Timeout_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('scan_enable', 1) -]) +@HCI_Command.command([('scan_enable', 1)]) class HCI_Write_Scan_Enable_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.18 Write Scan Enable Command @@ -1726,11 +2488,13 @@ class HCI_Write_Scan_Enable_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('page_scan_interval', 2), - ('page_scan_window', 2) -]) +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('page_scan_interval', 2), + ('page_scan_window', 2), + ] +) class HCI_Read_Page_Scan_Activity_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.19 Read Page Scan Activity Command @@ -1738,10 +2502,7 @@ class HCI_Read_Page_Scan_Activity_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('page_scan_interval', 2), - ('page_scan_window', 2) -]) +@HCI_Command.command([('page_scan_interval', 2), ('page_scan_window', 2)]) class HCI_Write_Page_Scan_Activity_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.20 Write Page Scan Activity Command @@ -1749,10 +2510,7 @@ class HCI_Write_Page_Scan_Activity_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('inquiry_scan_interval', 2), - ('inquiry_scan_window', 2) -]) +@HCI_Command.command([('inquiry_scan_interval', 2), ('inquiry_scan_window', 2)]) class HCI_Write_Inquiry_Scan_Activity_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.22 Write Inquiry Scan Activity Command @@ -1760,10 +2518,12 @@ class HCI_Write_Inquiry_Scan_Activity_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('class_of_device', {'size': 3, 'mapper': map_class_of_device}) -]) +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), + ] +) class HCI_Read_Class_Of_Device_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.25 Read Class of Device Command @@ -1771,9 +2531,7 @@ class HCI_Read_Class_Of_Device_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('class_of_device', {'size': 3, 'mapper': map_class_of_device}) -]) +@HCI_Command.command([('class_of_device', {'size': 3, 'mapper': map_class_of_device})]) class HCI_Write_Class_Of_Device_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.26 Write Class of Device Command @@ -1781,10 +2539,9 @@ class HCI_Write_Class_Of_Device_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('voice_setting', 2) -]) +@HCI_Command.command( + return_parameters_fields=[('status', STATUS_SPEC), ('voice_setting', 2)] +) class HCI_Read_Voice_Setting_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.27 Read Voice Setting Command @@ -1792,9 +2549,7 @@ class HCI_Read_Voice_Setting_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('voice_setting', 2) -]) +@HCI_Command.command([('voice_setting', 2)]) class HCI_Write_Voice_Setting_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.28 Write Voice Setting Command @@ -1802,16 +2557,15 @@ class HCI_Write_Voice_Setting_Command(HCI_Command): # ----------------------------------------------------------------------------- +@HCI_Command.command() class HCI_Read_Synchronous_Flow_Control_Enable_Command(HCI_Command): ''' - See Bluetooth spec @ 7.3.36 Write Synchronous Flow Control Enable Command + See Bluetooth spec @ 7.3.36 Read Synchronous Flow Control Enable Command ''' # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('synchronous_flow_control_enable', 1) -]) +@HCI_Command.command([('synchronous_flow_control_enable', 1)]) class HCI_Write_Synchronous_Flow_Control_Enable_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.37 Write Synchronous Flow Control Enable Command @@ -1819,12 +2573,14 @@ class HCI_Write_Synchronous_Flow_Control_Enable_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('host_acl_data_packet_length', 2), - ('host_synchronous_data_packet_length', 1), - ('host_total_num_acl_data_packets', 2), - ('host_total_num_synchronous_data_packets', 2) -]) +@HCI_Command.command( + [ + ('host_acl_data_packet_length', 2), + ('host_synchronous_data_packet_length', 1), + ('host_total_num_acl_data_packets', 2), + ('host_total_num_synchronous_data_packets', 2), + ] +) class HCI_Host_Buffer_Size_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.39 Host Buffer Size Command @@ -1833,14 +2589,11 @@ class HCI_Host_Buffer_Size_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ - ('handle', 2), - ('link_supervision_timeout', 2) - ], + fields=[('handle', 2), ('link_supervision_timeout', 2)], return_parameters_fields=[ ('status', STATUS_SPEC), ('handle', 2), - ] + ], ) class HCI_Write_Link_Supervision_Timeout_Command(HCI_Command): ''' @@ -1849,10 +2602,9 @@ class HCI_Write_Link_Supervision_Timeout_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('num_support_iac', 1) -]) +@HCI_Command.command( + return_parameters_fields=[('status', STATUS_SPEC), ('num_support_iac', 1)] +) class HCI_Read_Number_Of_Supported_IAC_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.43 Read Number Of Supported IAC Command @@ -1860,11 +2612,13 @@ class HCI_Read_Number_Of_Supported_IAC_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('num_current_iac', 1), - ('iac_lap', '*') # TODO: this should be parsed as an array -]) +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('num_current_iac', 1), + ('iac_lap', '*'), # TODO: this should be parsed as an array + ] +) class HCI_Read_Current_IAC_LAP_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.44 Read Current IAC LAP Command @@ -1872,9 +2626,7 @@ class HCI_Read_Current_IAC_LAP_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('scan_type', 1) -]) +@HCI_Command.command([('scan_type', 1)]) class HCI_Write_Inquiry_Scan_Type_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.48 Write Inquiry Scan Type Command @@ -1882,9 +2634,7 @@ class HCI_Write_Inquiry_Scan_Type_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('inquiry_mode', 1) -]) +@HCI_Command.command([('inquiry_mode', 1)]) class HCI_Write_Inquiry_Mode_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.50 Write Inquiry Mode Command @@ -1892,10 +2642,9 @@ class HCI_Write_Inquiry_Mode_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('page_scan_type', 1) -]) +@HCI_Command.command( + return_parameters_fields=[('status', STATUS_SPEC), ('page_scan_type', 1)] +) class HCI_Read_Page_Scan_Type_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.51 Read Page Scan Type Command @@ -1903,9 +2652,7 @@ class HCI_Read_Page_Scan_Type_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('page_scan_type', 1) -]) +@HCI_Command.command([('page_scan_type', 1)]) class HCI_Write_Page_Scan_Type_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.52 Write Page Scan Type Command @@ -1913,10 +2660,15 @@ class HCI_Write_Page_Scan_Type_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('fec_required', 1), - ('extended_inquiry_response', {'size': 240, 'serializer': lambda x: padded_bytes(x, 240)}) -]) +@HCI_Command.command( + [ + ('fec_required', 1), + ( + 'extended_inquiry_response', + {'size': 240, 'serializer': lambda x: padded_bytes(x, 240)}, + ), + ] +) class HCI_Write_Extended_Inquiry_Response_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.56 Write Extended Inquiry Response Command @@ -1924,9 +2676,7 @@ class HCI_Write_Extended_Inquiry_Response_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('simple_pairing_mode', 1) -]) +@HCI_Command.command([('simple_pairing_mode', 1)]) class HCI_Write_Simple_Pairing_Mode_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.59 Write Simple Pairing Mode Command @@ -1934,10 +2684,9 @@ class HCI_Write_Simple_Pairing_Mode_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('tx_power', -1) -]) +@HCI_Command.command( + return_parameters_fields=[('status', STATUS_SPEC), ('tx_power', -1)] +) class HCI_Read_Inquiry_Response_Transmit_Power_Level_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.61 Read Inquiry Response Transmit Power Level Command @@ -1945,10 +2694,9 @@ class HCI_Read_Inquiry_Response_Transmit_Power_Level_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('erroneous_data_reporting', 1) -]) +@HCI_Command.command( + return_parameters_fields=[('status', STATUS_SPEC), ('erroneous_data_reporting', 1)] +) class HCI_Read_Default_Erroneous_Data_Reporting_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.64 Read Default Erroneous Data Reporting Command @@ -1956,9 +2704,7 @@ class HCI_Read_Default_Erroneous_Data_Reporting_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('event_mask_page_2', 8) -]) +@HCI_Command.command([('event_mask_page_2', 8)]) class HCI_Set_Event_Mask_Page_2_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.69 Set Event Mask Page 2 Command @@ -1974,10 +2720,7 @@ class HCI_Read_LE_Host_Support_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('le_supported_host', 1), - ('simultaneous_le_host', 1) -]) +@HCI_Command.command([('le_supported_host', 1), ('simultaneous_le_host', 1)]) class HCI_Write_LE_Host_Support_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.79 Write LE Host Support Command @@ -1985,9 +2728,7 @@ class HCI_Write_LE_Host_Support_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('secure_connections_host_support', 1) -]) +@HCI_Command.command([('secure_connections_host_support', 1)]) class HCI_Write_Secure_Connections_Host_Support_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.92 Write Secure Connections Host Support Command @@ -1995,10 +2736,7 @@ class HCI_Write_Secure_Connections_Host_Support_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('authenticated_payload_timeout', 2) -]) +@HCI_Command.command([('connection_handle', 2), ('authenticated_payload_timeout', 2)]) class HCI_Write_Authenticated_Payload_Timeout_Command(HCI_Command): ''' See Bluetooth spec @ 7.3.94 Write Authenticated Payload Timeout Command @@ -2006,14 +2744,16 @@ class HCI_Write_Authenticated_Payload_Timeout_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('hci_version', 1), - ('hci_revsion', 2), - ('lmp_pal_version', 1), - ('manufacturer_name', 2), - ('lmp_pal_subversion', 2) -]) +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('hci_version', 1), + ('hci_subversion', 2), + ('lmp_version', 1), + ('company_identifier', 2), + ('lmp_subversion', 2), + ] +) class HCI_Read_Local_Version_Information_Command(HCI_Command): ''' See Bluetooth spec @ 7.4.1 Read Local Version Information Command @@ -2021,10 +2761,9 @@ class HCI_Read_Local_Version_Information_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('supported_commands', 64) -]) +@HCI_Command.command( + return_parameters_fields=[('status', STATUS_SPEC), ('supported_commands', 64)] +) class HCI_Read_Local_Supported_Commands_Command(HCI_Command): ''' See Bluetooth spec @ 7.4.2 Read Local Supported Commands Command @@ -2041,15 +2780,13 @@ class HCI_Read_Local_Supported_Features_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ - ('page_number', 1) - ], + fields=[('page_number', 1)], return_parameters_fields=[ - ('status', STATUS_SPEC), - ('page_number', 1), - ('maximum_page_number', 1), - ('extended_lmp_features', 8) - ] + ('status', STATUS_SPEC), + ('page_number', 1), + ('maximum_page_number', 1), + ('extended_lmp_features', 8), + ], ) class HCI_Read_Local_Extended_Features_Command(HCI_Command): ''' @@ -2058,13 +2795,15 @@ class HCI_Read_Local_Extended_Features_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('hc_acl_data_packet_length', 2), - ('hc_synchronous_data_packet_length', 1), - ('hc_total_num_acl_data_packets', 2), - ('hc_total_num_synchronous_data_packets', 2) -]) +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('hc_acl_data_packet_length', 2), + ('hc_synchronous_data_packet_length', 1), + ('hc_total_num_acl_data_packets', 2), + ('hc_total_num_synchronous_data_packets', 2), + ] +) class HCI_Read_Buffer_Size_Command(HCI_Command): ''' See Bluetooth spec @ 7.4.5 Read Buffer Size Command @@ -2072,10 +2811,12 @@ class HCI_Read_Buffer_Size_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address) -]) +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ] +) class HCI_Read_BD_ADDR_Command(HCI_Command): ''' See Bluetooth spec @ 7.4.6 Read BD_ADDR Command @@ -2092,14 +2833,23 @@ class HCI_Read_Local_Supported_Codecs_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command( - fields=[ - ('connection_handle', 2) - ], + fields=[('handle', 2)], + return_parameters_fields=[('status', STATUS_SPEC), ('handle', 2), ('rssi', -1)], +) +class HCI_Read_RSSI_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.5.4 Read RSSI Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + fields=[('connection_handle', 2)], return_parameters_fields=[ - ('status', STATUS_SPEC), + ('status', STATUS_SPEC), ('connection_handle', 2), - ('key_size', 1) - ] + ('key_size', 1), + ], ) class HCI_Read_Encryption_Key_Size_Command(HCI_Command): ''' @@ -2108,9 +2858,7 @@ class HCI_Read_Encryption_Key_Size_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('le_event_mask', 8) -]) +@HCI_Command.command([('le_event_mask', 8)]) class HCI_LE_Set_Event_Mask_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.1 LE Set Event Mask Command @@ -2118,11 +2866,13 @@ class HCI_LE_Set_Event_Mask_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command(return_parameters_fields=[ - ('status', STATUS_SPEC), - ('hc_le_acl_data_packet_length', 2), - ('hc_total_num_le_acl_data_packets', 1) -]) +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('hc_le_acl_data_packet_length', 2), + ('hc_total_num_le_acl_data_packets', 1), + ] +) class HCI_LE_Read_Buffer_Size_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.2 LE Read Buffer Size Command @@ -2130,9 +2880,26 @@ class HCI_LE_Read_Buffer_Size_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('random_address', lambda data, offset: Address.parse_address_with_type(data, offset, Address.RANDOM_DEVICE_ADDRESS)) -]) +@HCI_Command.command( + return_parameters_fields=[('status', STATUS_SPEC), ('le_features', 8)] +) +class HCI_LE_Read_Local_Supported_Features_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.3 LE Read Local Supported Features Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + [ + ( + 'random_address', + lambda data, offset: Address.parse_address_with_type( + data, offset, Address.RANDOM_DEVICE_ADDRESS + ), + ) + ] +) class HCI_LE_Set_Random_Address_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.4 LE Set Random Address Command @@ -2140,33 +2907,44 @@ class HCI_LE_Set_Random_Address_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('advertising_interval_min', 2), - ('advertising_interval_max', 2), - ('advertising_type', {'size': 1, 'mapper': lambda x: HCI_LE_Set_Advertising_Parameters_Command.advertising_type_name(x)}), - ('own_address_type', Address.ADDRESS_TYPE_SPEC), - ('peer_address_type', Address.ADDRESS_TYPE_SPEC), - ('peer_address', Address.parse_address_preceded_by_type), - ('advertising_channel_map', 1), - ('advertising_filter_policy', 1), -]) +@HCI_Command.command( + # pylint: disable=line-too-long,unnecessary-lambda + [ + ('advertising_interval_min', 2), + ('advertising_interval_max', 2), + ( + 'advertising_type', + { + 'size': 1, + 'mapper': lambda x: HCI_LE_Set_Advertising_Parameters_Command.advertising_type_name( + x + ), + }, + ), + ('own_address_type', OwnAddressType.TYPE_SPEC), + ('peer_address_type', Address.ADDRESS_TYPE_SPEC), + ('peer_address', Address.parse_address_preceded_by_type), + ('advertising_channel_map', 1), + ('advertising_filter_policy', 1), + ] +) class HCI_LE_Set_Advertising_Parameters_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.5 LE Set Advertising Parameters Command ''' - ADV_IND = 0x00 - ADV_DIRECT_IND = 0x01 - ADV_SCAN_IND = 0x02 + ADV_IND = 0x00 + ADV_DIRECT_IND = 0x01 + ADV_SCAN_IND = 0x02 ADV_NONCONN_IND = 0x03 - ADV_DIRECT_IND = 0x04 + ADV_DIRECT_IND_LOW_DUTY = 0x04 ADVERTISING_TYPE_NAMES = { - ADV_IND: 'ADV_IND', - ADV_DIRECT_IND: 'ADV_DIRECT_IND', - ADV_SCAN_IND: 'ADV_SCAN_IND', + ADV_IND: 'ADV_IND', + ADV_DIRECT_IND: 'ADV_DIRECT_IND', + ADV_SCAN_IND: 'ADV_SCAN_IND', ADV_NONCONN_IND: 'ADV_NONCONN_IND', - ADV_DIRECT_IND: 'ADV_DIRECT_IND' + ADV_DIRECT_IND_LOW_DUTY: 'ADV_DIRECT_IND_LOW_DUTY', } @classmethod @@ -2176,19 +2954,26 @@ class HCI_LE_Set_Advertising_Parameters_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command() -class HCI_LE_Read_Advertising_Channel_Tx_Power_Command(HCI_Command): +class HCI_LE_Read_Advertising_Physical_Channel_Tx_Power_Command(HCI_Command): ''' - See Bluetooth spec @ 7.8.6 LE Read Advertising Channel Tx Power Command + See Bluetooth spec @ 7.8.6 LE Read Advertising Physical Channel Tx Power Command ''' # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('advertising_data', { - 'parser': HCI_Object.parse_length_prefixed_bytes, - 'serializer': functools.partial(HCI_Object.serialize_length_prefixed_bytes, padded_size=32) - }) -]) +@HCI_Command.command( + [ + ( + 'advertising_data', + { + 'parser': HCI_Object.parse_length_prefixed_bytes, + 'serializer': functools.partial( + HCI_Object.serialize_length_prefixed_bytes, padded_size=32 + ), + }, + ) + ] +) class HCI_LE_Set_Advertising_Data_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.7 LE Set Advertising Data Command @@ -2196,12 +2981,19 @@ class HCI_LE_Set_Advertising_Data_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('scan_response_data', { - 'parser': HCI_Object.parse_length_prefixed_bytes, - 'serializer': functools.partial(HCI_Object.serialize_length_prefixed_bytes, padded_size=32) - }) -]) +@HCI_Command.command( + [ + ( + 'scan_response_data', + { + 'parser': HCI_Object.parse_length_prefixed_bytes, + 'serializer': functools.partial( + HCI_Object.serialize_length_prefixed_bytes, padded_size=32 + ), + }, + ) + ] +) class HCI_LE_Set_Scan_Response_Data_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.8 LE Set Scan Response Data Command @@ -2209,9 +3001,7 @@ class HCI_LE_Set_Scan_Response_Data_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('advertising_enable', 1) -]) +@HCI_Command.command([('advertising_enable', 1)]) class HCI_LE_Set_Advertising_Enable_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.9 LE Set Advertising Enable Command @@ -2219,31 +3009,36 @@ class HCI_LE_Set_Advertising_Enable_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('le_scan_type', 1), - ('le_scan_interval', 2), - ('le_scan_window', 2), - ('own_address_type', Address.ADDRESS_TYPE_SPEC), - ('scanning_filter_policy', 1) -]) +@HCI_Command.command( + [ + ('le_scan_type', 1), + ('le_scan_interval', 2), + ('le_scan_window', 2), + ('own_address_type', OwnAddressType.TYPE_SPEC), + ('scanning_filter_policy', 1), + ] +) class HCI_LE_Set_Scan_Parameters_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.10 LE Set Scan Parameters Command ''' + PASSIVE_SCANNING = 0 - ACTIVE_SCANNING = 1 + ACTIVE_SCANNING = 1 - BASIC_UNFILTERED_POLICY = 0x00 - BASIC_FILTERED_POLICY = 0x01 + BASIC_UNFILTERED_POLICY = 0x00 + BASIC_FILTERED_POLICY = 0x01 EXTENDED_UNFILTERED_POLICY = 0x02 - EXTENDED_FILTERED_POLICY = 0x03 + EXTENDED_FILTERED_POLICY = 0x03 # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('le_scan_enable', 1), - ('filter_duplicates', 1), -]) +@HCI_Command.command( + [ + ('le_scan_enable', 1), + ('filter_duplicates', 1), + ] +) class HCI_LE_Set_Scan_Enable_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.11 LE Set Scan Enable Command @@ -2251,20 +3046,22 @@ class HCI_LE_Set_Scan_Enable_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('le_scan_interval', 2), - ('le_scan_window', 2), - ('initiator_filter_policy', 1), - ('peer_address_type', Address.ADDRESS_TYPE_SPEC), - ('peer_address', Address.parse_address_preceded_by_type), - ('own_address_type', Address.ADDRESS_TYPE_SPEC), - ('conn_interval_min', 2), - ('conn_interval_max', 2), - ('conn_latency', 2), - ('supervision_timeout', 2), - ('minimum_ce_length', 2), - ('maximum_ce_length', 2) -]) +@HCI_Command.command( + [ + ('le_scan_interval', 2), + ('le_scan_window', 2), + ('initiator_filter_policy', 1), + ('peer_address_type', Address.ADDRESS_TYPE_SPEC), + ('peer_address', Address.parse_address_preceded_by_type), + ('own_address_type', OwnAddressType.TYPE_SPEC), + ('connection_interval_min', 2), + ('connection_interval_max', 2), + ('max_latency', 2), + ('supervision_timeout', 2), + ('min_ce_length', 2), + ('max_ce_length', 2), + ] +) class HCI_LE_Create_Connection_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.12 LE Create Connection Command @@ -2281,52 +3078,58 @@ class HCI_LE_Create_Connection_Cancel_Command(HCI_Command): # ----------------------------------------------------------------------------- @HCI_Command.command() -class HCI_LE_Read_White_List_Size_Command(HCI_Command): +class HCI_LE_Read_Filter_Accept_List_Size_Command(HCI_Command): ''' - See Bluetooth spec @ 7.8.14 LE Read White List Size Command + See Bluetooth spec @ 7.8.14 LE Read Filter Accept List Size Command ''' # ----------------------------------------------------------------------------- @HCI_Command.command() -class HCI_LE_Clear_White_List_Command(HCI_Command): +class HCI_LE_Clear_Filter_Accept_List_Command(HCI_Command): ''' - See Bluetooth spec @ 7.8.15 LE Clear White List Command + See Bluetooth spec @ 7.8.15 LE Clear Filter Accept List Command ''' # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('address_type', Address.ADDRESS_TYPE_SPEC), - ('address', Address.parse_address_preceded_by_type) -]) -class HCI_LE_Add_Device_To_White_List_Command(HCI_Command): +@HCI_Command.command( + [ + ('address_type', Address.ADDRESS_TYPE_SPEC), + ('address', Address.parse_address_preceded_by_type), + ] +) +class HCI_LE_Add_Device_To_Filter_Accept_List_Command(HCI_Command): ''' - See Bluetooth spec @ 7.8.16 LE Add Device To White List Command + See Bluetooth spec @ 7.8.16 LE Add Device To Filter Accept List Command ''' # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('address_type', Address.ADDRESS_TYPE_SPEC), - ('address', Address.parse_address_preceded_by_type) -]) -class HCI_LE_Remove_Device_From_White_List_Command(HCI_Command): +@HCI_Command.command( + [ + ('address_type', Address.ADDRESS_TYPE_SPEC), + ('address', Address.parse_address_preceded_by_type), + ] +) +class HCI_LE_Remove_Device_From_Filter_Accept_List_Command(HCI_Command): ''' - See Bluetooth spec @ 7.8.17 LE Remove Device From White List Command + See Bluetooth spec @ 7.8.17 LE Remove Device From Filter Accept List Command ''' # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('conn_interval_min', 2), - ('conn_interval_max', 2), - ('conn_latency', 2), - ('supervision_timeout', 2), - ('minimum_ce_length', 2), - ('maximum_ce_length', 2) -]) +@HCI_Command.command( + [ + ('connection_handle', 2), + ('connection_interval_min', 2), + ('connection_interval_max', 2), + ('max_latency', 2), + ('supervision_timeout', 2), + ('min_ce_length', 2), + ('max_ce_length', 2), + ] +) class HCI_LE_Connection_Update_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.18 LE Connection Update Command @@ -2334,9 +3137,7 @@ class HCI_LE_Connection_Update_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2) -]) +@HCI_Command.command([('connection_handle', 2)]) class HCI_LE_Read_Remote_Features_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.21 LE Read Remote Features Command @@ -2344,24 +3145,34 @@ class HCI_LE_Read_Remote_Features_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('random_number', 8), - ('encrypted_diversifier', 2), - ('long_term_key', 16) -]) -class HCI_LE_Start_Encryption_Command(HCI_Command): +@HCI_Command.command( + return_parameters_fields=[("status", STATUS_SPEC), ("random_number", 8)] +) +class HCI_LE_Rand_Command(HCI_Command): + """ + See Bluetooth spec @ 7.8.23 LE Rand Command + """ + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + [ + ('connection_handle', 2), + ('random_number', 8), + ('encrypted_diversifier', 2), + ('long_term_key', 16), + ] +) +class HCI_LE_Enable_Encryption_Command(HCI_Command): ''' - See Bluetooth spec @ 7.8.24 LE Start Encryption Command - (renamed to "LE Enable Encryption Command" in version 5.2 of the specification) + See Bluetooth spec @ 7.8.24 LE Enable Encryption Command + (renamed from "LE Start Encryption Command" in version prior to 5.2 of the + specification) ''' # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('long_term_key', 16) -]) +@HCI_Command.command([('connection_handle', 2), ('long_term_key', 16)]) class HCI_LE_Long_Term_Key_Request_Reply_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.25 LE Long Term Key Request Reply Command @@ -2369,9 +3180,7 @@ class HCI_LE_Long_Term_Key_Request_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2) -]) +@HCI_Command.command([('connection_handle', 2)]) class HCI_LE_Long_Term_Key_Request_Negative_Reply_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.26 LE Long Term Key Request Negative Reply Command @@ -2387,15 +3196,17 @@ class HCI_LE_Read_Supported_States_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('interval_min', 2), - ('interval_max', 2), - ('latency', 2), - ('timeout', 2), - ('minimum_ce_length', 2), - ('maximum_ce_length', 2) -]) +@HCI_Command.command( + [ + ('connection_handle', 2), + ('interval_min', 2), + ('interval_max', 2), + ('max_latency', 2), + ('timeout', 2), + ('min_ce_length', 2), + ('max_ce_length', 2), + ] +) class HCI_LE_Remote_Connection_Parameter_Request_Reply_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.31 LE Remote Connection Parameter Request Reply Command @@ -2403,21 +3214,50 @@ class HCI_LE_Remote_Connection_Parameter_Request_Reply_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('connection_handle', 2), - ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}) -]) +@HCI_Command.command( + [ + ('connection_handle', 2), + ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}), + ] +) class HCI_LE_Remote_Connection_Parameter_Request_Negative_Reply_Command(HCI_Command): ''' - See Bluetooth spec @ 7.8.32 LE Remote Connection Parameter Request Negative Reply Command + See Bluetooth spec @ 7.8.32 LE Remote Connection Parameter Request Negative Reply + Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + fields=[ + ('connection_handle', 2), + ('tx_octets', 2), + ('tx_time', 2), + ], + return_parameters_fields=[('status', STATUS_SPEC), ('connection_handle', 2)], +) +class HCI_LE_Set_Data_Length_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.33 LE Set Data Length Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('suggested_max_tx_octets', 2), + ('suggested_max_tx_time', 2), + ] +) +class HCI_LE_Read_Suggested_Default_Data_Length_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.34 LE Read Suggested Default Data Length Command ''' # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('suggested_max_tx_octets', 2), - ('suggested_max_tx_time', 2) -]) +@HCI_Command.command([('suggested_max_tx_octets', 2), ('suggested_max_tx_time', 2)]) class HCI_LE_Write_Suggested_Default_Data_Length_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.35 LE Write Suggested Default Data Length Command @@ -2425,12 +3265,14 @@ class HCI_LE_Write_Suggested_Default_Data_Length_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('peer_identity_address_type', Address.ADDRESS_TYPE_SPEC), - ('peer_identity_address', Address.parse_address_preceded_by_type), - ('peer_irk', 16), - ('local_irk', 16), -]) +@HCI_Command.command( + [ + ('peer_identity_address_type', Address.ADDRESS_TYPE_SPEC), + ('peer_identity_address', Address.parse_address_preceded_by_type), + ('peer_irk', 16), + ('local_irk', 16), + ] +) class HCI_LE_Add_Device_To_Resolving_List_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.38 LE Add Device To Resolving List Command @@ -2446,34 +3288,765 @@ class HCI_LE_Clear_Resolving_List_Command(HCI_Command): # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('all_phys', 1), - ('tx_phys', 1), - ('rx_phys', 1) -]) +@HCI_Command.command([('address_resolution_enable', 1)]) +class HCI_LE_Set_Address_Resolution_Enable_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.44 LE Set Address Resolution Enable Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command([('rpa_timeout', 2)]) +class HCI_LE_Set_Resolvable_Private_Address_Timeout_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.45 LE Set Resolvable Private Address Timeout Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('supported_max_tx_octets', 2), + ('supported_max_tx_time', 2), + ('supported_max_rx_octets', 2), + ('supported_max_rx_time', 2), + ] +) +class HCI_LE_Read_Maximum_Data_Length_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.46 LE Read Maximum Data Length Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + fields=[('connection_handle', 2)], + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('tx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), + ('rx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), + ], +) +class HCI_LE_Read_PHY_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.47 LE Read PHY Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + [ + ( + 'all_phys', + { + 'size': 1, + 'mapper': lambda x: bit_flags_to_strings( + x, HCI_LE_Set_Default_PHY_Command.ANY_PHY_BIT_NAMES + ), + }, + ), + ( + 'tx_phys', + { + 'size': 1, + 'mapper': lambda x: bit_flags_to_strings(x, HCI_LE_PHY_BIT_NAMES), + }, + ), + ( + 'rx_phys', + { + 'size': 1, + 'mapper': lambda x: bit_flags_to_strings(x, HCI_LE_PHY_BIT_NAMES), + }, + ), + ] +) class HCI_LE_Set_Default_PHY_Command(HCI_Command): ''' See Bluetooth spec @ 7.8.48 LE Set Default PHY Command ''' + ANY_TX_PHY_BIT = 0 + ANY_RX_PHY_BIT = 1 + + ANY_PHY_BIT_NAMES = ['Any TX', 'Any RX'] + # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('address_resolution_enable', 1) -]) -class HCI_LE_Set_Address_Resolution_Enable_Command(HCI_Command): +@HCI_Command.command( + [ + ('connection_handle', 2), + ( + 'all_phys', + { + 'size': 1, + 'mapper': lambda x: bit_flags_to_strings( + x, HCI_LE_Set_PHY_Command.ANY_PHY_BIT_NAMES + ), + }, + ), + ( + 'tx_phys', + { + 'size': 1, + 'mapper': lambda x: bit_flags_to_strings(x, HCI_LE_PHY_BIT_NAMES), + }, + ), + ( + 'rx_phys', + { + 'size': 1, + 'mapper': lambda x: bit_flags_to_strings(x, HCI_LE_PHY_BIT_NAMES), + }, + ), + ('phy_options', 2), + ] +) +class HCI_LE_Set_PHY_Command(HCI_Command): ''' - See Bluetooth spec @ 7.8.44 LE Set Address Resolution Enable Command + See Bluetooth spec @ 7.8.49 LE Set PHY Command ''' + ANY_TX_PHY_BIT = 0 + ANY_RX_PHY_BIT = 1 + + ANY_PHY_BIT_NAMES = ['Any TX', 'Any RX'] + # ----------------------------------------------------------------------------- -@HCI_Command.command([ - ('rpa_timeout', 2) -]) -class HCI_LE_Set_Resolvable_Private_Address_Timeout_Command(HCI_Command): +@HCI_Command.command( + [ + ('advertising_handle', 1), + ( + 'random_address', + lambda data, offset: Address.parse_address_with_type( + data, offset, Address.RANDOM_DEVICE_ADDRESS + ), + ), + ] +) +class HCI_LE_Set_Advertising_Set_Random_Address_Command(HCI_Command): ''' - See Bluetooth spec @ 7.8.45 LE Set Resolvable Private Address Timeout Command + See Bluetooth spec @ 7.8.52 LE Set Advertising Set Random Address Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + # pylint: disable=line-too-long,unnecessary-lambda + fields=[ + ('advertising_handle', 1), + ( + 'advertising_event_properties', + { + 'size': 2, + 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Parameters_Command.advertising_properties_string( + x + ), + }, + ), + ('primary_advertising_interval_min', 3), + ('primary_advertising_interval_max', 3), + ( + 'primary_advertising_channel_map', + { + 'size': 1, + 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Parameters_Command.channel_map_string( + x + ), + }, + ), + ('own_address_type', OwnAddressType.TYPE_SPEC), + ('peer_address_type', Address.ADDRESS_TYPE_SPEC), + ('peer_address', Address.parse_address_preceded_by_type), + ('advertising_filter_policy', 1), + ('advertising_tx_power', 1), + ('primary_advertising_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), + ('secondary_advertising_max_skip', 1), + ('secondary_advertising_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), + ('advertising_sid', 1), + ('scan_request_notification_enable', 1), + ], + return_parameters_fields=[('status', STATUS_SPEC), ('selected_tx__power', 1)], +) +class HCI_LE_Set_Extended_Advertising_Parameters_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.53 LE Set Extended Advertising Parameters Command + ''' + + CONNECTABLE_ADVERTISING = 0 + SCANNABLE_ADVERTISING = 1 + DIRECTED_ADVERTISING = 2 + HIGH_DUTY_CYCLE_DIRECTED_CONNECTABLE_ADVERTISING = 3 + USE_LEGACY_ADVERTISING_PDUS = 4 + ANONYMOUS_ADVERTISING = 5 + INCLUDE_TX_POWER = 6 + + ADVERTISING_PROPERTIES_NAMES = ( + 'CONNECTABLE_ADVERTISING', + 'SCANNABLE_ADVERTISING', + 'DIRECTED_ADVERTISING', + 'HIGH_DUTY_CYCLE_DIRECTED_CONNECTABLE_ADVERTISING', + 'USE_LEGACY_ADVERTISING_PDUS', + 'ANONYMOUS_ADVERTISING', + 'INCLUDE_TX_POWER', + ) + + CHANNEL_37 = 0 + CHANNEL_38 = 1 + CHANNEL_39 = 2 + + CHANNEL_NAMES = ('37', '38', '39') + + @classmethod + def advertising_properties_string(cls, properties): + # pylint: disable=line-too-long + return f'[{",".join(bit_flags_to_strings(properties, cls.ADVERTISING_PROPERTIES_NAMES))}]' + + @classmethod + def channel_map_string(cls, channel_map): + return f'[{",".join(bit_flags_to_strings(channel_map, cls.CHANNEL_NAMES))}]' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + # pylint: disable=line-too-long,unnecessary-lambda + [ + ('advertising_handle', 1), + ( + 'operation', + { + 'size': 1, + 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.operation_name( + x + ), + }, + ), + ('fragment_preference', 1), + ( + 'advertising_data', + { + 'parser': HCI_Object.parse_length_prefixed_bytes, + 'serializer': functools.partial( + HCI_Object.serialize_length_prefixed_bytes + ), + }, + ), + ] +) +class HCI_LE_Set_Extended_Advertising_Data_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.54 LE Set Extended Advertising Data Command + ''' + + INTERMEDIATE_FRAGMENT = 0x00 + FIRST_FRAGMENT = 0x01 + LAST_FRAGMENT = 0x02 + COMPLETE_DATA = 0x03 + UNCHANGED_DATA = 0x04 + + OPERATION_NAMES = { + INTERMEDIATE_FRAGMENT: 'INTERMEDIATE_FRAGMENT', + FIRST_FRAGMENT: 'FIRST_FRAGMENT', + LAST_FRAGMENT: 'LAST_FRAGMENT', + COMPLETE_DATA: 'COMPLETE_DATA', + UNCHANGED_DATA: 'UNCHANGED_DATA', + } + + @classmethod + def operation_name(cls, operation): + return name_or_number(cls.OPERATION_NAMES, operation) + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + # pylint: disable=line-too-long,unnecessary-lambda + [ + ('advertising_handle', 1), + ( + 'operation', + { + 'size': 1, + 'mapper': lambda x: HCI_LE_Set_Extended_Advertising_Data_Command.operation_name( + x + ), + }, + ), + ('fragment_preference', 1), + ( + 'scan_response_data', + { + 'parser': HCI_Object.parse_length_prefixed_bytes, + 'serializer': functools.partial( + HCI_Object.serialize_length_prefixed_bytes + ), + }, + ), + ] +) +class HCI_LE_Set_Extended_Scan_Response_Data_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.55 LE Set Extended Scan Response Data Command + ''' + + INTERMEDIATE_FRAGMENT = 0x00 + FIRST_FRAGMENT = 0x01 + LAST_FRAGMENT = 0x02 + COMPLETE_DATA = 0x03 + + OPERATION_NAMES = { + INTERMEDIATE_FRAGMENT: 'INTERMEDIATE_FRAGMENT', + FIRST_FRAGMENT: 'FIRST_FRAGMENT', + LAST_FRAGMENT: 'LAST_FRAGMENT', + COMPLETE_DATA: 'COMPLETE_DATA', + } + + @classmethod + def operation_name(cls, operation): + return name_or_number(cls.OPERATION_NAMES, operation) + + +# ----------------------------------------------------------------------------- +@HCI_Command.command(fields=None) +class HCI_LE_Set_Extended_Advertising_Enable_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.56 LE Set Extended Advertising Enable Command + ''' + + @classmethod + def from_parameters(cls, parameters): + enable = parameters[0] + num_sets = parameters[1] + advertising_handles = [] + durations = [] + max_extended_advertising_events = [] + offset = 2 + for _ in range(num_sets): + advertising_handles.append(parameters[offset]) + durations.append(struct.unpack_from('<H', parameters, offset + 1)[0]) + max_extended_advertising_events.append(parameters[offset + 3]) + offset += 4 + + return cls( + enable, advertising_handles, durations, max_extended_advertising_events + ) + + def __init__( + self, enable, advertising_handles, durations, max_extended_advertising_events + ): + super().__init__(HCI_LE_SET_EXTENDED_ADVERTISING_ENABLE_COMMAND) + self.enable = enable + self.advertising_handles = advertising_handles + self.durations = durations + self.max_extended_advertising_events = max_extended_advertising_events + + self.parameters = bytes([enable, len(advertising_handles)]) + b''.join( + [ + struct.pack( + '<BHB', + advertising_handles[i], + durations[i], + max_extended_advertising_events[i], + ) + for i in range(len(advertising_handles)) + ] + ) + + def __str__(self): + fields = [('enable:', self.enable)] + for i, advertising_handle in enumerate(self.advertising_handles): + fields.append( + (f'advertising_handle[{i}]: ', advertising_handle) + ) + fields.append((f'duration[{i}]: ', self.durations[i])) + fields.append( + ( + f'max_extended_advertising_events[{i}]:', + self.max_extended_advertising_events[i], + ) + ) + + return ( + color(self.name, 'green') + + ':\n' + + '\n'.join( + [color(field[0], 'cyan') + ' ' + str(field[1]) for field in fields] + ) + ) + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('max_advertising_data_length', 2), + ] +) +class HCI_LE_Read_Maximum_Advertising_Data_Length_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.57 LE Read Maximum Advertising Data Length Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + return_parameters_fields=[ + ('status', STATUS_SPEC), + ('num_supported_advertising_sets', 1), + ] +) +class HCI_LE_Read_Number_Of_Supported_Advertising_Sets_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.58 LE Read Number of Supported Advertising Sets Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command([('advertising_handle', 1)]) +class HCI_LE_Remove_Advertising_Set_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.59 LE Remove Advertising Set Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command() +class HCI_LE_Clear_Advertising_Sets_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.60 LE Clear Advertising Sets Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command([('enable', 1), ('advertising_handle', 1)]) +class HCI_LE_Set_Periodic_Advertising_Enable_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.63 LE Set Periodic Advertising Enable Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command(fields=None) +class HCI_LE_Set_Extended_Scan_Parameters_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.64 LE Set Extended Scan Parameters Command + ''' + + PASSIVE_SCANNING = 0 + ACTIVE_SCANNING = 1 + + BASIC_UNFILTERED_POLICY = 0x00 + BASIC_FILTERED_POLICY = 0x01 + EXTENDED_UNFILTERED_POLICY = 0x02 + EXTENDED_FILTERED_POLICY = 0x03 + + @classmethod + def from_parameters(cls, parameters): + own_address_type = parameters[0] + scanning_filter_policy = parameters[1] + scanning_phys = parameters[2] + + phy_bits_set = bin(scanning_phys).count('1') + scan_types = [] + scan_intervals = [] + scan_windows = [] + for i in range(phy_bits_set): + scan_types.append(parameters[3 + (5 * i)]) + scan_intervals.append( + struct.unpack_from('<H', parameters, 3 + (5 * i) + 1)[0] + ) + scan_windows.append( + struct.unpack_from('<H', parameters, 3 + (5 * i) + 3)[0] + ) + + return cls( + own_address_type=own_address_type, + scanning_filter_policy=scanning_filter_policy, + scanning_phys=scanning_phys, + scan_types=scan_types, + scan_intervals=scan_intervals, + scan_windows=scan_windows, + ) + + def __init__( + self, + own_address_type, + scanning_filter_policy, + scanning_phys, + scan_types, + scan_intervals, + scan_windows, + ): + super().__init__(HCI_LE_SET_EXTENDED_SCAN_PARAMETERS_COMMAND) + self.own_address_type = own_address_type + self.scanning_filter_policy = scanning_filter_policy + self.scanning_phys = scanning_phys + self.scan_types = scan_types + self.scan_intervals = scan_intervals + self.scan_windows = scan_windows + + self.parameters = bytes( + [own_address_type, scanning_filter_policy, scanning_phys] + ) + phy_bits_set = bin(scanning_phys).count('1') + for i in range(phy_bits_set): + self.parameters += struct.pack( + '<BHH', scan_types[i], scan_intervals[i], scan_windows[i] + ) + + def __str__(self): + scanning_phys_strs = bit_flags_to_strings( + self.scanning_phys, HCI_LE_PHY_BIT_NAMES + ) + fields = [ + ( + 'own_address_type: ', + Address.address_type_name(self.own_address_type), + ), + ('scanning_filter_policy:', self.scanning_filter_policy), + ('scanning_phys: ', ','.join(scanning_phys_strs)), + ] + for (i, scanning_phy_str) in enumerate(scanning_phys_strs): + fields.append( + ( + f'{scanning_phy_str}.scan_type: ', + 'PASSIVE' + if self.scan_types[i] == self.PASSIVE_SCANNING + else 'ACTIVE', + ) + ) + fields.append( + (f'{scanning_phy_str}.scan_interval:', self.scan_intervals[i]) + ) + fields.append((f'{scanning_phy_str}.scan_window: ', self.scan_windows[i])) + + return ( + color(self.name, 'green') + + ':\n' + + '\n'.join( + [color(field[0], 'cyan') + ' ' + str(field[1]) for field in fields] + ) + ) + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + [('enable', 1), ('filter_duplicates', 1), ('duration', 2), ('period', 2)] +) +class HCI_LE_Set_Extended_Scan_Enable_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.65 LE Set Extended Scan Enable Command + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Command.command(fields=None) +class HCI_LE_Extended_Create_Connection_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.66 LE Extended Create Connection Command + ''' + + @classmethod + def from_parameters(cls, parameters): + initiator_filter_policy = parameters[0] + own_address_type = parameters[1] + peer_address_type = parameters[2] + peer_address = Address.parse_address_preceded_by_type(parameters, 3)[1] + initiating_phys = parameters[9] + + phy_bits_set = bin(initiating_phys).count('1') + + def read_parameter_list(offset): + return [ + struct.unpack_from('<H', parameters, offset + 16 * i)[0] + for i in range(phy_bits_set) + ] + + return cls( + initiator_filter_policy=initiator_filter_policy, + own_address_type=own_address_type, + peer_address_type=peer_address_type, + peer_address=peer_address, + initiating_phys=initiating_phys, + scan_intervals=read_parameter_list(10), + scan_windows=read_parameter_list(12), + connection_interval_mins=read_parameter_list(14), + connection_interval_maxs=read_parameter_list(16), + max_latencies=read_parameter_list(18), + supervision_timeouts=read_parameter_list(20), + min_ce_lengths=read_parameter_list(22), + max_ce_lengths=read_parameter_list(24), + ) + + def __init__( + self, + initiator_filter_policy, + own_address_type, + peer_address_type, + peer_address, + initiating_phys, + scan_intervals, + scan_windows, + connection_interval_mins, + connection_interval_maxs, + max_latencies, + supervision_timeouts, + min_ce_lengths, + max_ce_lengths, + ): + super().__init__(HCI_LE_EXTENDED_CREATE_CONNECTION_COMMAND) + self.initiator_filter_policy = initiator_filter_policy + self.own_address_type = own_address_type + self.peer_address_type = peer_address_type + self.peer_address = peer_address + self.initiating_phys = initiating_phys + self.scan_intervals = scan_intervals + self.scan_windows = scan_windows + self.connection_interval_mins = connection_interval_mins + self.connection_interval_maxs = connection_interval_maxs + self.max_latencies = max_latencies + self.supervision_timeouts = supervision_timeouts + self.min_ce_lengths = min_ce_lengths + self.max_ce_lengths = max_ce_lengths + + self.parameters = ( + bytes([initiator_filter_policy, own_address_type, peer_address_type]) + + bytes(peer_address) + + bytes([initiating_phys]) + ) + + phy_bits_set = bin(initiating_phys).count('1') + for i in range(phy_bits_set): + self.parameters += struct.pack( + '<HHHHHHHH', + scan_intervals[i], + scan_windows[i], + connection_interval_mins[i], + connection_interval_maxs[i], + max_latencies[i], + supervision_timeouts[i], + min_ce_lengths[i], + max_ce_lengths[i], + ) + + def __str__(self): + initiating_phys_strs = bit_flags_to_strings( + self.initiating_phys, HCI_LE_PHY_BIT_NAMES + ) + fields = [ + ('initiator_filter_policy:', self.initiator_filter_policy), + ( + 'own_address_type: ', + OwnAddressType.type_name(self.own_address_type), + ), + ( + 'peer_address_type: ', + Address.address_type_name(self.peer_address_type), + ), + ('peer_address: ', str(self.peer_address)), + ('initiating_phys: ', ','.join(initiating_phys_strs)), + ] + for (i, initiating_phys_str) in enumerate(initiating_phys_strs): + fields.append( + ( + f'{initiating_phys_str}.scan_interval: ', + self.scan_intervals[i], + ) + ) + fields.append( + ( + f'{initiating_phys_str}.scan_window: ', + self.scan_windows[i], + ) + ) + fields.append( + ( + f'{initiating_phys_str}.connection_interval_min:', + self.connection_interval_mins[i], + ) + ) + fields.append( + ( + f'{initiating_phys_str}.connection_interval_max:', + self.connection_interval_maxs[i], + ) + ) + fields.append( + ( + f'{initiating_phys_str}.max_latency: ', + self.max_latencies[i], + ) + ) + fields.append( + ( + f'{initiating_phys_str}.supervision_timeout: ', + self.supervision_timeouts[i], + ) + ) + fields.append( + ( + f'{initiating_phys_str}.min_ce_length: ', + self.min_ce_lengths[i], + ) + ) + fields.append( + ( + f'{initiating_phys_str}.max_ce_length: ', + self.max_ce_lengths[i], + ) + ) + + return ( + color(self.name, 'green') + + ':\n' + + '\n'.join( + [color(field[0], 'cyan') + ' ' + str(field[1]) for field in fields] + ) + ) + + +# ----------------------------------------------------------------------------- +@HCI_Command.command( + [ + ('peer_identity_address_type', Address.ADDRESS_TYPE_SPEC), + ('peer_identity_address', Address.parse_address_preceded_by_type), + ( + 'privacy_mode', + { + 'size': 1, + # pylint: disable-next=unnecessary-lambda + 'mapper': lambda x: HCI_LE_Set_Privacy_Mode_Command.privacy_mode_name( + x + ), + }, + ), + ] +) +class HCI_LE_Set_Privacy_Mode_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.77 LE Set Privacy Mode Command + ''' + + NETWORK_PRIVACY_MODE = 0x00 + DEVICE_PRIVACY_MODE = 0x01 + + PRIVACY_MODE_NAMES = { + NETWORK_PRIVACY_MODE: 'NETWORK_PRIVACY_MODE', + DEVICE_PRIVACY_MODE: 'DEVICE_PRIVACY_MODE', + } + + @classmethod + def privacy_mode_name(cls, privacy_mode): + return name_or_number(cls.PRIVACY_MODE_NAMES, privacy_mode) + + +# ----------------------------------------------------------------------------- +@HCI_Command.command([('bit_number', 1), ('bit_value', 1)]) +class HCI_LE_Set_Host_Feature_Command(HCI_Command): + ''' + See Bluetooth spec @ 7.8.115 LE Set Host Feature Command ''' @@ -2484,12 +4057,13 @@ class HCI_Event(HCI_Packet): ''' See Bluetooth spec @ Vol 2, Part E - 5.4.4 HCI Event Packet ''' - hci_packet_type = HCI_EVENT_PACKET - event_classes = {} - meta_event_classes = {} + + hci_packet_type = HCI_EVENT_PACKET + event_classes: Dict[int, Type[HCI_Event]] = {} + meta_event_classes: Dict[int, Type[HCI_LE_Meta_Event]] = {} @staticmethod - def event(fields=[]): + def event(fields=()): ''' Decorator used to declare and register subclasses ''' @@ -2504,6 +4078,7 @@ class HCI_Event(HCI_Packet): # Patch the __init__ method to fix the event_code def init(self, parameters=None, **kwargs): return HCI_Event.__init__(self, cls.event_code, parameters, **kwargs) + cls.__init__ = init # Register a factory for this class @@ -2514,27 +4089,28 @@ class HCI_Event(HCI_Packet): return inner @staticmethod - def registered(cls): - cls.name = cls.__name__.upper() - cls.event_code = key_with_value(HCI_EVENT_NAMES, cls.name) - if cls.event_code is None: + def registered(event_class): + event_class.name = event_class.__name__.upper() + event_class.event_code = key_with_value(HCI_EVENT_NAMES, event_class.name) + if event_class.event_code is None: raise KeyError('event not found in HCI_EVENT_NAMES') # Register a factory for this class - HCI_Event.event_classes[cls.event_code] = cls + HCI_Event.event_classes[event_class.event_code] = event_class - return cls + return event_class @staticmethod def from_bytes(packet): event_code = packet[1] - length = packet[2] + length = packet[2] parameters = packet[3:] if len(parameters) != length: raise ValueError('invalid packet length') if event_code == HCI_LE_META_EVENT: - # We do this dispatch here and not in the subclass in order to avoid call loops + # We do this dispatch here and not in the subclass in order to avoid call + # loops subevent_code = parameters[0] cls = HCI_Event.meta_event_classes.get(subevent_code) if cls is None: @@ -2575,6 +4151,9 @@ class HCI_Event(HCI_Packet): parameters = b'' if self.parameters is None else self.parameters return bytes([HCI_EVENT_PACKET, self.event_code, len(parameters)]) + parameters + def __bytes__(self): + return self.to_bytes() + def __str__(self): result = color(self.name, 'magenta') if fields := getattr(self, 'fields', None): @@ -2592,7 +4171,7 @@ class HCI_LE_Meta_Event(HCI_Event): ''' @staticmethod - def event(fields=[]): + def event(fields=()): ''' Decorator used to declare and register subclasses ''' @@ -2606,7 +4185,10 @@ class HCI_LE_Meta_Event(HCI_Event): # Patch the __init__ method to fix the subevent_code def init(self, parameters=None, **kwargs): - return HCI_LE_Meta_Event.__init__(self, cls.subevent_code, parameters, **kwargs) + return HCI_LE_Meta_Event.__init__( + self, cls.subevent_code, parameters, **kwargs + ) + cls.__init__ = init # Register a factory for this class @@ -2631,7 +4213,9 @@ class HCI_LE_Meta_Event(HCI_Event): def __init__(self, subevent_code, parameters, **kwargs): self.subevent_code = subevent_code if parameters is None and (fields := getattr(self, 'fields', None)) and kwargs: - parameters = bytes([subevent_code]) + HCI_Object.dict_to_bytes(kwargs, fields) + parameters = bytes([subevent_code]) + HCI_Object.dict_to_bytes( + kwargs, fields + ) super().__init__(HCI_LE_META_EVENT, parameters, **kwargs) # Override the name in order to adopt the subevent name instead @@ -2648,17 +4232,22 @@ class HCI_LE_Meta_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_LE_Meta_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('role', {'size': 1, 'mapper': lambda x: 'CENTRAL' if x == 0 else 'PERIPHERAL'}), - ('peer_address_type', Address.ADDRESS_TYPE_SPEC), - ('peer_address', Address.parse_address_preceded_by_type), - ('conn_interval', 2), - ('conn_latency', 2), - ('supervision_timeout', 2), - ('master_clock_accuracy', 1) -]) +@HCI_LE_Meta_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ( + 'role', + {'size': 1, 'mapper': lambda x: 'CENTRAL' if x == 0 else 'PERIPHERAL'}, + ), + ('peer_address_type', Address.ADDRESS_TYPE_SPEC), + ('peer_address', Address.parse_address_preceded_by_type), + ('connection_interval', 2), + ('peripheral_latency', 2), + ('supervision_timeout', 2), + ('central_clock_accuracy', 1), + ] +) class HCI_LE_Connection_Complete_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.1 LE Connection Complete Event @@ -2670,75 +4259,104 @@ class HCI_LE_Advertising_Report_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.2 LE Advertising Report Event ''' + subevent_code = HCI_LE_ADVERTISING_REPORT_EVENT # Event Types - ADV_IND = 0x00 - ADV_DIRECT_IND = 0x01 - ADV_SCAN_IND = 0x02 + ADV_IND = 0x00 + ADV_DIRECT_IND = 0x01 + ADV_SCAN_IND = 0x02 ADV_NONCONN_IND = 0x03 - SCAN_RSP = 0x04 + SCAN_RSP = 0x04 EVENT_TYPE_NAMES = { - ADV_IND: 'ADV_IND', # Connectable and scannable undirected advertising - ADV_DIRECT_IND: 'ADV_DIRECT_IND', # Connectable directed advertising - ADV_SCAN_IND: 'ADV_SCAN_IND', # Scannable undirected advertising + ADV_IND: 'ADV_IND', # Connectable and scannable undirected advertising + ADV_DIRECT_IND: 'ADV_DIRECT_IND', # Connectable directed advertising + ADV_SCAN_IND: 'ADV_SCAN_IND', # Scannable undirected advertising ADV_NONCONN_IND: 'ADV_NONCONN_IND', # Non connectable undirected advertising - SCAN_RSP: 'SCAN_RSP' # Scan Response + SCAN_RSP: 'SCAN_RSP', # Scan Response } - REPORT_FIELDS = [ - ('event_type', 1), - ('address_type', Address.ADDRESS_TYPE_SPEC), - ('address', Address.parse_address_preceded_by_type), - ('data', {'parser': HCI_Object.parse_length_prefixed_bytes, 'serializer': HCI_Object.serialize_length_prefixed_bytes}), - ('rssi', -1) - ] + class Report(HCI_Object): + FIELDS = [ + ('event_type', 1), + ('address_type', Address.ADDRESS_TYPE_SPEC), + ('address', Address.parse_address_preceded_by_type), + ( + 'data', + { + 'parser': HCI_Object.parse_length_prefixed_bytes, + 'serializer': HCI_Object.serialize_length_prefixed_bytes, + }, + ), + ('rssi', -1), + ] + + @classmethod + def from_parameters(cls, parameters, offset): + return cls.from_bytes(parameters, offset, cls.FIELDS) + + def event_type_string(self): + return HCI_LE_Advertising_Report_Event.event_type_name(self.event_type) + + def to_string(self, indentation='', _=None): + return super().to_string( + indentation, + { + 'event_type': HCI_LE_Advertising_Report_Event.event_type_name, + 'address_type': Address.address_type_name, + 'data': lambda x: str(AdvertisingData.from_bytes(x)), + }, + ) @classmethod def event_type_name(cls, event_type): return name_or_number(cls.EVENT_TYPE_NAMES, event_type) - @staticmethod - def from_parameters(parameters): + @classmethod + def from_parameters(cls, parameters): num_reports = parameters[1] reports = [] offset = 2 for _ in range(num_reports): - report = HCI_Object.from_bytes(parameters, offset, HCI_LE_Advertising_Report_Event.REPORT_FIELDS) + report = cls.Report.from_parameters(parameters, offset) offset += 10 + len(report.data) reports.append(report) - return HCI_LE_Advertising_Report_Event(reports) + return cls(reports) def __init__(self, reports): self.reports = reports[:] # Serialize the fields - parameters = bytes([HCI_LE_ADVERTISING_REPORT_EVENT, len(reports)]) + b''.join([bytes(report) for report in reports]) + parameters = bytes([HCI_LE_ADVERTISING_REPORT_EVENT, len(reports)]) + b''.join( + [bytes(report) for report in reports] + ) super().__init__(self.subevent_code, parameters) def __str__(self): - reports = '\n'.join([report.to_string(' ', { - 'event_type': self.event_type_name, - 'address_type': Address.address_type_name, - 'data': lambda x: str(AdvertisingData.from_bytes(x)) - }) for report in self.reports]) + reports = '\n'.join( + [f'{i}:\n{report.to_string(" ")}' for i, report in enumerate(self.reports)] + ) return f'{color(self.subevent_name(self.subevent_code), "magenta")}:\n{reports}' -HCI_Event.meta_event_classes[HCI_LE_ADVERTISING_REPORT_EVENT] = HCI_LE_Advertising_Report_Event +HCI_Event.meta_event_classes[ + HCI_LE_ADVERTISING_REPORT_EVENT +] = HCI_LE_Advertising_Report_Event # ----------------------------------------------------------------------------- -@HCI_LE_Meta_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('conn_interval', 2), - ('conn_latency', 2), - ('supervision_timeout', 2) -]) +@HCI_LE_Meta_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('connection_interval', 2), + ('peripheral_latency', 2), + ('supervision_timeout', 2), + ] +) class HCI_LE_Connection_Update_Complete_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.3 LE Connection Update Complete Event @@ -2746,11 +4364,9 @@ class HCI_LE_Connection_Update_Complete_Event(HCI_LE_Meta_Event): # ----------------------------------------------------------------------------- -@HCI_LE_Meta_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('le_features', 8) -]) +@HCI_LE_Meta_Event.event( + [('status', STATUS_SPEC), ('connection_handle', 2), ('le_features', 8)] +) class HCI_LE_Read_Remote_Features_Complete_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.4 LE Read Remote Features Complete Event @@ -2758,11 +4374,9 @@ class HCI_LE_Read_Remote_Features_Complete_Event(HCI_LE_Meta_Event): # ----------------------------------------------------------------------------- -@HCI_LE_Meta_Event.event([ - ('connection_handle', 2), - ('random_number', 8), - ('encryption_diversifier', 2) -]) +@HCI_LE_Meta_Event.event( + [('connection_handle', 2), ('random_number', 8), ('encryption_diversifier', 2)] +) class HCI_LE_Long_Term_Key_Request_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.5 LE Long Term Key Request Event @@ -2770,13 +4384,15 @@ class HCI_LE_Long_Term_Key_Request_Event(HCI_LE_Meta_Event): # ----------------------------------------------------------------------------- -@HCI_LE_Meta_Event.event([ - ('connection_handle', 2), - ('interval_min', 2), - ('interval_max', 2), - ('latency', 2), - ('timeout', 2) -]) +@HCI_LE_Meta_Event.event( + [ + ('connection_handle', 2), + ('interval_min', 2), + ('interval_max', 2), + ('max_latency', 2), + ('timeout', 2), + ] +) class HCI_LE_Remote_Connection_Parameter_Request_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.6 LE Remote Connection Parameter Request Event @@ -2784,13 +4400,15 @@ class HCI_LE_Remote_Connection_Parameter_Request_Event(HCI_LE_Meta_Event): # ----------------------------------------------------------------------------- -@HCI_LE_Meta_Event.event([ - ('connection_handle', 2), - ('max_tx_octets', 2), - ('max_tx_time', 2), - ('max_rx_octets', 2), - ('max_rx_time', 2), -]) +@HCI_LE_Meta_Event.event( + [ + ('connection_handle', 2), + ('max_tx_octets', 2), + ('max_tx_time', 2), + ('max_rx_octets', 2), + ('max_rx_time', 2), + ] +) class HCI_LE_Data_Length_Change_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.7 LE Data Length Change Event @@ -2798,19 +4416,24 @@ class HCI_LE_Data_Length_Change_Event(HCI_LE_Meta_Event): # ----------------------------------------------------------------------------- -@HCI_LE_Meta_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('role', {'size': 1, 'mapper': lambda x: 'CENTRAL' if x == 0 else 'PERIPHERAL'}), - ('peer_address_type', Address.ADDRESS_TYPE_SPEC), - ('peer_address', Address.parse_address_preceded_by_type), - ('local_resolvable_private_address', Address.parse_address), - ('peer_resolvable_private_address', Address.parse_address), - ('conn_interval', 2), - ('conn_latency', 2), - ('supervision_timeout', 2), - ('master_clock_accuracy', 1) -]) +@HCI_LE_Meta_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ( + 'role', + {'size': 1, 'mapper': lambda x: 'CENTRAL' if x == 0 else 'PERIPHERAL'}, + ), + ('peer_address_type', Address.ADDRESS_TYPE_SPEC), + ('peer_address', Address.parse_address_preceded_by_type), + ('local_resolvable_private_address', Address.parse_address), + ('peer_resolvable_private_address', Address.parse_address), + ('connection_interval', 2), + ('peripheral_latency', 2), + ('supervision_timeout', 2), + ('central_clock_accuracy', 1), + ] +) class HCI_LE_Enhanced_Connection_Complete_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.10 LE Enhanced Connection Complete Event @@ -2818,12 +4441,14 @@ class HCI_LE_Enhanced_Connection_Complete_Event(HCI_LE_Meta_Event): # ----------------------------------------------------------------------------- -@HCI_LE_Meta_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('tx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), - ('rx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}) -]) +@HCI_LE_Meta_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('tx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), + ('rx_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), + ] +) class HCI_LE_PHY_Update_Complete_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.12 LE PHY Update Complete Event @@ -2831,10 +4456,153 @@ class HCI_LE_PHY_Update_Complete_Event(HCI_LE_Meta_Event): # ----------------------------------------------------------------------------- -@HCI_LE_Meta_Event.event([ - ('connection_handle', 2), - ('channel_selection_algorithm', 1) -]) +class HCI_LE_Extended_Advertising_Report_Event(HCI_LE_Meta_Event): + ''' + See Bluetooth spec @ 7.7.65.13 LE Extended Advertising Report Event + ''' + + subevent_code = HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT + + # Event types flags + CONNECTABLE_ADVERTISING = 0 + SCANNABLE_ADVERTISING = 1 + DIRECTED_ADVERTISING = 2 + SCAN_RESPONSE = 3 + LEGACY_ADVERTISING_PDU_USED = 4 + + DATA_COMPLETE = 0x00 + DATA_INCOMPLETE_MORE_TO_COME = 0x01 + DATA_INCOMPLETE_TRUNCATED_NO_MORE_TO_COME = 0x02 + + EVENT_TYPE_FLAG_NAMES = ( + 'CONNECTABLE_ADVERTISING', + 'SCANNABLE_ADVERTISING', + 'DIRECTED_ADVERTISING', + 'SCAN_RESPONSE', + 'LEGACY_ADVERTISING_PDU_USED', + ) + + LEGACY_PDU_TYPE_MAP = { + 0b0011: HCI_LE_Advertising_Report_Event.ADV_IND, + 0b0101: HCI_LE_Advertising_Report_Event.ADV_DIRECT_IND, + 0b0010: HCI_LE_Advertising_Report_Event.ADV_SCAN_IND, + 0b0000: HCI_LE_Advertising_Report_Event.ADV_NONCONN_IND, + 0b1011: HCI_LE_Advertising_Report_Event.SCAN_RSP, + 0b1010: HCI_LE_Advertising_Report_Event.SCAN_RSP, + } + + NO_ADI_FIELD_PROVIDED = 0xFF + TX_POWER_INFORMATION_NOT_AVAILABLE = 0x7F + RSSI_NOT_AVAILABLE = 0x7F + ANONYMOUS_ADDRESS_TYPE = 0xFF + UNRESOLVED_RESOLVABLE_ADDRESS_TYPE = 0xFE + + class Report(HCI_Object): + FIELDS = [ + ('event_type', 2), + ('address_type', Address.ADDRESS_TYPE_SPEC), + ('address', Address.parse_address_preceded_by_type), + ('primary_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), + ('secondary_phy', {'size': 1, 'mapper': HCI_Constant.le_phy_name}), + ('advertising_sid', 1), + ('tx_power', 1), + ('rssi', -1), + ('periodic_advertising_interval', 2), + ('direct_address_type', Address.ADDRESS_TYPE_SPEC), + ('direct_address', Address.parse_address_preceded_by_type), + ( + 'data', + { + 'parser': HCI_Object.parse_length_prefixed_bytes, + 'serializer': HCI_Object.serialize_length_prefixed_bytes, + }, + ), + ] + + @classmethod + def from_parameters(cls, parameters, offset): + return cls.from_bytes(parameters, offset, cls.FIELDS) + + def event_type_string(self): + return HCI_LE_Extended_Advertising_Report_Event.event_type_string( + self.event_type + ) + + def to_string(self, indentation='', _=None): + # pylint: disable=line-too-long + return super().to_string( + indentation, + { + 'event_type': HCI_LE_Extended_Advertising_Report_Event.event_type_string, + 'address_type': Address.address_type_name, + 'data': lambda x: str(AdvertisingData.from_bytes(x)), + }, + ) + + @staticmethod + def event_type_string(event_type): + event_type_flags = bit_flags_to_strings( + event_type & 0x1F, + HCI_LE_Extended_Advertising_Report_Event.EVENT_TYPE_FLAG_NAMES, + ) + event_type_flags.append( + ('COMPLETE', 'INCOMPLETE+', 'INCOMPLETE#', '?')[(event_type >> 5) & 3] + ) + + if event_type & ( + 1 << HCI_LE_Extended_Advertising_Report_Event.LEGACY_ADVERTISING_PDU_USED + ): + legacy_pdu_type = ( + HCI_LE_Extended_Advertising_Report_Event.LEGACY_PDU_TYPE_MAP.get( + event_type & 0x0F + ) + ) + if legacy_pdu_type is not None: + # pylint: disable=line-too-long + legacy_info_string = f'({HCI_LE_Advertising_Report_Event.event_type_name(legacy_pdu_type)})' + else: + legacy_info_string = '' + else: + legacy_info_string = '' + + return f'0x{event_type:04X} [{",".join(event_type_flags)}]{legacy_info_string}' + + @classmethod + def from_parameters(cls, parameters): + num_reports = parameters[1] + reports = [] + offset = 2 + for _ in range(num_reports): + report = cls.Report.from_parameters(parameters, offset) + offset += 24 + len(report.data) + reports.append(report) + + return cls(reports) + + def __init__(self, reports): + self.reports = reports[:] + + # Serialize the fields + parameters = bytes( + [HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT, len(reports)] + ) + b''.join([bytes(report) for report in reports]) + + super().__init__(self.subevent_code, parameters) + + def __str__(self): + reports = '\n'.join( + [f'{i}:\n{report.to_string(" ")}' for i, report in enumerate(self.reports)] + ) + return f'{color(self.subevent_name(self.subevent_code), "magenta")}:\n{reports}' + + +HCI_Event.meta_event_classes[ + HCI_LE_EXTENDED_ADVERTISING_REPORT_EVENT +] = HCI_LE_Extended_Advertising_Report_Event + + +# ----------------------------------------------------------------------------- +@HCI_LE_Meta_Event.event([('connection_handle', 2), ('channel_selection_algorithm', 1)]) class HCI_LE_Channel_Selection_Algorithm_Event(HCI_LE_Meta_Event): ''' See Bluetooth spec @ 7.7.65.20 LE Channel Selection Algorithm Event @@ -2842,9 +4610,7 @@ class HCI_LE_Channel_Selection_Algorithm_Event(HCI_LE_Meta_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC) -]) +@HCI_Event.event([('status', STATUS_SPEC)]) class HCI_Inquiry_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.1 Inquiry Complete Event @@ -2859,12 +4625,12 @@ class HCI_Inquiry_Result_Event(HCI_Event): ''' RESPONSE_FIELDS = [ - ('bd_addr', Address.parse_address), + ('bd_addr', Address.parse_address), ('page_scan_repetition_mode', 1), - ('reserved', 1), - ('reserved', 1), - ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), - ('clock_offset', 2) + ('reserved', 1), + ('reserved', 1), + ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), + ('clock_offset', 2), ] @staticmethod @@ -2873,7 +4639,9 @@ class HCI_Inquiry_Result_Event(HCI_Event): responses = [] offset = 1 for _ in range(num_responses): - response = HCI_Object.from_bytes(parameters, offset, HCI_Inquiry_Result_Event.RESPONSE_FIELDS) + response = HCI_Object.from_bytes( + parameters, offset, HCI_Inquiry_Result_Event.RESPONSE_FIELDS + ) offset += 14 responses.append(response) @@ -2883,36 +4651,49 @@ class HCI_Inquiry_Result_Event(HCI_Event): self.responses = responses[:] # Serialize the fields - parameters = bytes([HCI_INQUIRY_RESULT_EVENT, len(responses)]) + b''.join([bytes(response) for response in responses]) + parameters = bytes([HCI_INQUIRY_RESULT_EVENT, len(responses)]) + b''.join( + [bytes(response) for response in responses] + ) super().__init__(HCI_INQUIRY_RESULT_EVENT, parameters) def __str__(self): - responses = '\n'.join([response.to_string(indentation=' ') for response in self.responses]) + responses = '\n'.join( + [response.to_string(indentation=' ') for response in self.responses] + ) return f'{color("HCI_INQUIRY_RESULT_EVENT", "magenta")}:\n{responses}' # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('bd_addr', Address.parse_address), - ('link_type', {'size': 1, 'mapper': lambda x: HCI_Connection_Complete_Event.link_type_name(x)}), - ('encryption_enabled', 1) -]) +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('bd_addr', Address.parse_address), + ( + 'link_type', + { + 'size': 1, + # pylint: disable-next=unnecessary-lambda + 'mapper': lambda x: HCI_Connection_Complete_Event.link_type_name(x), + }, + ), + ('encryption_enabled', 1), + ] +) class HCI_Connection_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.3 Connection Complete Event ''' - SCO_LINK_TYPE = 0x00 - ACL_LINK_TYPE = 0x01 + SCO_LINK_TYPE = 0x00 + ACL_LINK_TYPE = 0x01 ESCO_LINK_TYPE = 0x02 LINK_TYPE_NAMES = { - SCO_LINK_TYPE: 'SCO', - ACL_LINK_TYPE: 'ACL', - ESCO_LINK_TYPE: 'eSCO' + SCO_LINK_TYPE: 'SCO', + ACL_LINK_TYPE: 'ACL', + ESCO_LINK_TYPE: 'eSCO', } @staticmethod @@ -2921,11 +4702,20 @@ class HCI_Connection_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address), - ('class_of_device', 3), - ('link_type', {'size': 1, 'mapper': lambda x: HCI_Connection_Complete_Event.link_type_name(x)}) -]) +@HCI_Event.event( + [ + ('bd_addr', Address.parse_address), + ('class_of_device', 3), + ( + 'link_type', + { + 'size': 1, + # pylint: disable-next=unnecessary-lambda + 'mapper': lambda x: HCI_Connection_Complete_Event.link_type_name(x), + }, + ), + ] +) class HCI_Connection_Request_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.4 Connection Request Event @@ -2933,11 +4723,13 @@ class HCI_Connection_Request_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}) -]) +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('reason', {'size': 1, 'mapper': HCI_Constant.error_name}), + ] +) class HCI_Disconnection_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.5 Disconnection Complete Event @@ -2945,10 +4737,7 @@ class HCI_Disconnection_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2) -]) +@HCI_Event.event([('status', STATUS_SPEC), ('connection_handle', 2)]) class HCI_Authentication_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.6 Authentication Complete Event @@ -2956,11 +4745,13 @@ class HCI_Authentication_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address), - ('remote_name', {'size': 248, 'mapper': map_null_terminated_utf8_string}) -]) +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ('remote_name', {'size': 248, 'mapper': map_null_terminated_utf8_string}), + ] +) class HCI_Remote_Name_Request_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.7 Remote Name Request Complete Event @@ -2968,37 +4759,48 @@ class HCI_Remote_Name_Request_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('encryption_enabled', {'size': 1, 'mapper': lambda x: HCI_Encryption_Change_Event.encryption_enabled_name(x)}) -]) +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ( + 'encryption_enabled', + { + 'size': 1, + # pylint: disable-next=unnecessary-lambda + 'mapper': lambda x: HCI_Encryption_Change_Event.encryption_enabled_name( + x + ), + }, + ), + ] +) class HCI_Encryption_Change_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.8 Encryption Change Event ''' - OFF = 0x00 + OFF = 0x00 E0_OR_AES_CCM = 0x01 - AES_CCM = 0x02 + AES_CCM = 0x02 - ENCYRPTION_ENABLED_NAMES = { - OFF: 'OFF', + ENCRYPTION_ENABLED_NAMES = { + OFF: 'OFF', E0_OR_AES_CCM: 'E0_OR_AES_CCM', - AES_CCM: 'AES_CCM' + AES_CCM: 'AES_CCM', } @staticmethod def encryption_enabled_name(encryption_enabled): - return name_or_number(HCI_Encryption_Change_Event.ENCYRPTION_ENABLED_NAMES, encryption_enabled) + return name_or_number( + HCI_Encryption_Change_Event.ENCRYPTION_ENABLED_NAMES, encryption_enabled + ) # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('lmp_features', 8) -]) +@HCI_Event.event( + [('status', STATUS_SPEC), ('connection_handle', 2), ('lmp_features', 8)] +) class HCI_Read_Remote_Supported_Features_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.11 Read Remote Supported Features Complete Event @@ -3006,13 +4808,15 @@ class HCI_Read_Remote_Supported_Features_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('version', 1), - ('manufacturer_name', 2), - ('subversion', 2) -]) +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('version', 1), + ('manufacturer_name', 2), + ('subversion', 2), + ] +) class HCI_Read_Remote_Version_Information_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.12 Read Remote Version Information Complete Event @@ -3020,77 +4824,103 @@ class HCI_Read_Remote_Version_Information_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('num_hci_command_packets', 1), - ('command_opcode', {'size': 2, 'mapper': HCI_Command.command_name}), - ('return_parameters', '*') -]) +@HCI_Event.event( + [ + ('num_hci_command_packets', 1), + ('command_opcode', {'size': 2, 'mapper': HCI_Command.command_name}), + ('return_parameters', '*'), + ] +) class HCI_Command_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.14 Command Complete Event ''' + return_parameters = b'' + def map_return_parameters(self, return_parameters): - # Map simple 'status' return parameters to their named constant form - if type(return_parameters) is bytes and len(return_parameters) == 1: + '''Map simple 'status' return parameters to their named constant form''' + + if isinstance(return_parameters, bytes) and len(return_parameters) == 1: # Byte-array form return HCI_Constant.status_name(return_parameters[0]) - elif type(return_parameters) is int: + + if isinstance(return_parameters, int): # Already converted to an integer status code return HCI_Constant.status_name(return_parameters) - else: - return return_parameters + + return return_parameters @staticmethod def from_parameters(parameters): self = HCI_Command_Complete_Event.__new__(HCI_Command_Complete_Event) HCI_Event.__init__(self, self.event_code, parameters) - HCI_Object.init_from_bytes(self, parameters, 0, HCI_Command_Complete_Event.fields) + HCI_Object.init_from_bytes( + self, parameters, 0, HCI_Command_Complete_Event.fields + ) # Parse the return parameters - if type(self.return_parameters) is bytes and len(self.return_parameters) == 1: - # All commands with 1-byte return parameters return a 'status' field, convert it to an integer + if ( + isinstance(self.return_parameters, bytes) + and len(self.return_parameters) == 1 + ): + # All commands with 1-byte return parameters return a 'status' field, + # convert it to an integer self.return_parameters = self.return_parameters[0] else: cls = HCI_Command.command_classes.get(self.command_opcode) if cls and cls.return_parameters_fields: - self.return_parameters = HCI_Object.from_bytes(self.return_parameters, 0, cls.return_parameters_fields) + self.return_parameters = HCI_Object.from_bytes( + self.return_parameters, 0, cls.return_parameters_fields + ) self.return_parameters.fields = cls.return_parameters_fields return self def __str__(self): - return f'{color(self.name, "magenta")}:\n' + HCI_Object.format_fields(self.__dict__, self.fields, ' ', { - 'return_parameters': self.map_return_parameters - }) + return f'{color(self.name, "magenta")}:\n' + HCI_Object.format_fields( + self.__dict__, + self.fields, + ' ', + {'return_parameters': self.map_return_parameters}, + ) # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', {'size': 1, 'mapper': lambda x: HCI_Command_Status_Event.status_name(x)}), - ('num_hci_command_packets', 1), - ('command_opcode', {'size': 2, 'mapper': HCI_Command.command_name}) -]) +@HCI_Event.event( + [ + ( + 'status', + # pylint: disable-next=unnecessary-lambda + {'size': 1, 'mapper': lambda x: HCI_Command_Status_Event.status_name(x)}, + ), + ('num_hci_command_packets', 1), + ('command_opcode', {'size': 2, 'mapper': HCI_Command.command_name}), + ] +) class HCI_Command_Status_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.15 Command Complete Event ''' + PENDING = 0 @staticmethod def status_name(status): if status == HCI_Command_Status_Event.PENDING: return 'PENDING' - else: - return HCI_Constant.error_name(status) + + return HCI_Constant.error_name(status) # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address), - ('new_role', {'size': 1, 'mapper': HCI_Constant.role_name}) -]) +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('bd_addr', Address.parse_address), + ('new_role', {'size': 1, 'mapper': HCI_Constant.role_name}), + ] +) class HCI_Role_Change_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.18 Role Change Event @@ -3135,34 +4965,47 @@ class HCI_Number_Of_Completed_Packets_Event(HCI_Event): def __str__(self): lines = [ color(self.name, 'magenta') + ':', - color(' number_of_handles: ', 'cyan') + f'{len(self.connection_handles)}' + color(' number_of_handles: ', 'cyan') + + f'{len(self.connection_handles)}', ] - for i in range(len(self.connection_handles)): - lines.append(color(f' connection_handle[{i}]: ', 'cyan') + f'{self.connection_handles[i]}') - lines.append(color(f' num_completed_packets[{i}]: ', 'cyan') + f'{self.num_completed_packets[i]}') + for i, connection_handle in enumerate(self.connection_handles): + lines.append( + color(f' connection_handle[{i}]: ', 'cyan') + + f'{connection_handle}' + ) + lines.append( + color(f' num_completed_packets[{i}]: ', 'cyan') + + f'{self.num_completed_packets[i]}' + ) return '\n'.join(lines) # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('current_mode', {'size': 1, 'mapper': lambda x: HCI_Mode_Change_Event.mode_name(x)}), - ('interval', 2) -]) +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ( + 'current_mode', + # pylint: disable-next=unnecessary-lambda + {'size': 1, 'mapper': lambda x: HCI_Mode_Change_Event.mode_name(x)}, + ), + ('interval', 2), + ] +) class HCI_Mode_Change_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.20 Mode Change Event ''' ACTIVE_MODE = 0x00 - HOLD_MODE = 0x01 - SNIFF_MODE = 0x02 + HOLD_MODE = 0x01 + SNIFF_MODE = 0x02 MODE_NAMES = { ACTIVE_MODE: 'ACTIVE_MODE', - HOLD_MODE: 'HOLD_MODE', - SNIFF_MODE: 'SNIFF_MODE' + HOLD_MODE: 'HOLD_MODE', + SNIFF_MODE: 'SNIFF_MODE', } @staticmethod @@ -3171,9 +5014,7 @@ class HCI_Mode_Change_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address) -]) +@HCI_Event.event([('bd_addr', Address.parse_address)]) class HCI_PIN_Code_Request_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.22 PIN Code Request Event @@ -3181,9 +5022,7 @@ class HCI_PIN_Code_Request_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address) -]) +@HCI_Event.event([('bd_addr', Address.parse_address)]) class HCI_Link_Key_Request_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.24 7.7.23 Link Key Request Event @@ -3191,11 +5030,13 @@ class HCI_Link_Key_Request_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address), - ('link_key', 16), - ('key_type', {'size': 1, 'mapper': HCI_Constant.link_key_type_name}) -]) +@HCI_Event.event( + [ + ('bd_addr', Address.parse_address), + ('link_key', 16), + ('key_type', {'size': 1, 'mapper': HCI_Constant.link_key_type_name}), + ] +) class HCI_Link_Key_Notification_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.24 Link Key Notification Event @@ -3203,10 +5044,7 @@ class HCI_Link_Key_Notification_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('connection_handle', 2), - ('lmp_max_slots', 1) -]) +@HCI_Event.event([('connection_handle', 2), ('lmp_max_slots', 1)]) class HCI_Max_Slots_Change_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.27 Max Slots Change Event @@ -3214,11 +5052,9 @@ class HCI_Max_Slots_Change_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('clock_offset', 2) -]) +@HCI_Event.event( + [('status', STATUS_SPEC), ('connection_handle', 2), ('clock_offset', 2)] +) class HCI_Read_Clock_Offset_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.28 Read Clock Offset Complete Event @@ -3226,11 +5062,9 @@ class HCI_Read_Clock_Offset_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('packet_type', 2) -]) +@HCI_Event.event( + [('status', STATUS_SPEC), ('connection_handle', 2), ('packet_type', 2)] +) class HCI_Connection_Packet_Type_Changed_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.29 Connection Packet Type Changed Event @@ -3238,10 +5072,7 @@ class HCI_Connection_Packet_Type_Changed_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address), - ('page_scan_repetition_mode', 1) -]) +@HCI_Event.event([('bd_addr', Address.parse_address), ('page_scan_repetition_mode', 1)]) class HCI_Page_Scan_Repetition_Mode_Change_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.31 Page Scan Repetition Mode Change Event @@ -3250,18 +5081,18 @@ class HCI_Page_Scan_Repetition_Mode_Change_Event(HCI_Event): # ----------------------------------------------------------------------------- @HCI_Event.registered -class HCI_Inquiry_Result_With_Rssi_Event(HCI_Event): +class HCI_Inquiry_Result_With_RSSI_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.33 Inquiry Result with RSSI Event ''' RESPONSE_FIELDS = [ - ('bd_addr', Address.parse_address), + ('bd_addr', Address.parse_address), ('page_scan_repetition_mode', 1), - ('reserved', 1), - ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), - ('clock_offset', 2), - ('rssi', -1) + ('reserved', 1), + ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), + ('clock_offset', 2), + ('rssi', -1), ] @staticmethod @@ -3270,33 +5101,41 @@ class HCI_Inquiry_Result_With_Rssi_Event(HCI_Event): responses = [] offset = 1 for _ in range(num_responses): - response = HCI_Object.from_bytes(parameters, offset, HCI_Inquiry_Result_With_Rssi_Event.RESPONSE_FIELDS) + response = HCI_Object.from_bytes( + parameters, offset, HCI_Inquiry_Result_With_RSSI_Event.RESPONSE_FIELDS + ) offset += 14 responses.append(response) - return HCI_Inquiry_Result_With_Rssi_Event(responses) + return HCI_Inquiry_Result_With_RSSI_Event(responses) def __init__(self, responses): self.responses = responses[:] # Serialize the fields - parameters = bytes([HCI_INQUIRY_RESULT_WITH_RSSI_EVENT, len(responses)]) + b''.join([bytes(response) for response in responses]) + parameters = bytes( + [HCI_INQUIRY_RESULT_WITH_RSSI_EVENT, len(responses)] + ) + b''.join([bytes(response) for response in responses]) super().__init__(HCI_INQUIRY_RESULT_WITH_RSSI_EVENT, parameters) def __str__(self): - responses = '\n'.join([response.to_string(indentation=' ') for response in self.responses]) + responses = '\n'.join( + [response.to_string(indentation=' ') for response in self.responses] + ) return f'{color("HCI_INQUIRY_RESULT_WITH_RSSI_EVENT", "magenta")}:\n{responses}' # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('page_number', 1), - ('maximum_page_number', 1), - ('extended_lmp_features', 8) -]) +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('page_number', 1), + ('maximum_page_number', 1), + ('extended_lmp_features', 8), + ] +) class HCI_Read_Remote_Extended_Features_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.34 Read Remote Extended Features Complete Event @@ -3304,17 +5143,38 @@ class HCI_Read_Remote_Extended_Features_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('bd_addr', Address.parse_address), - ('link_type', {'size': 1, 'mapper': lambda x: HCI_Synchronous_Connection_Complete_Event.link_type_name(x)}), - ('transmission_interval', 1), - ('retransmission_window', 1), - ('rx_packet_length', 2), - ('tx_packet_length', 2), - ('air_mode', {'size': 1, 'mapper': lambda x: HCI_Synchronous_Connection_Complete_Event.air_mode_name(x)}), -]) +@HCI_Event.event( + # pylint: disable=line-too-long + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('bd_addr', Address.parse_address), + ( + 'link_type', + { + 'size': 1, + # pylint: disable-next=unnecessary-lambda + 'mapper': lambda x: HCI_Synchronous_Connection_Complete_Event.link_type_name( + x + ), + }, + ), + ('transmission_interval', 1), + ('retransmission_window', 1), + ('rx_packet_length', 2), + ('tx_packet_length', 2), + ( + 'air_mode', + { + 'size': 1, + # pylint: disable-next=unnecessary-lambda + 'mapper': lambda x: HCI_Synchronous_Connection_Complete_Event.air_mode_name( + x + ), + }, + ), + ] +) class HCI_Synchronous_Connection_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.35 Synchronous Connection Complete Event @@ -3324,40 +5184,46 @@ class HCI_Synchronous_Connection_Complete_Event(HCI_Event): ESCO_CONNECTION_LINK_TYPE = 0x02 LINK_TYPE_NAMES = { - SCO_CONNECTION_LINK_TYPE: 'SCO', - ESCO_CONNECTION_LINK_TYPE: 'eSCO' + SCO_CONNECTION_LINK_TYPE: 'SCO', + ESCO_CONNECTION_LINK_TYPE: 'eSCO', } - U_LAW_LOG_AIR_MODE = 0x00 - A_LAW_LOG_AIR_MORE = 0x01 - CVSD_AIR_MODE = 0x02 + U_LAW_LOG_AIR_MODE = 0x00 + A_LAW_LOG_AIR_MORE = 0x01 + CVSD_AIR_MODE = 0x02 TRANSPARENT_DATA_AIR_MODE = 0x03 AIR_MODE_NAMES = { - U_LAW_LOG_AIR_MODE: 'u-law log', - A_LAW_LOG_AIR_MORE: 'A-law log', - CVSD_AIR_MODE: 'CVSD', - TRANSPARENT_DATA_AIR_MODE: 'Transparend Data' + U_LAW_LOG_AIR_MODE: 'u-law log', + A_LAW_LOG_AIR_MORE: 'A-law log', + CVSD_AIR_MODE: 'CVSD', + TRANSPARENT_DATA_AIR_MODE: 'Transparent Data', } @staticmethod def link_type_name(link_type): - return name_or_number(HCI_Synchronous_Connection_Complete_Event.LINK_TYPE_NAMES, link_type) + return name_or_number( + HCI_Synchronous_Connection_Complete_Event.LINK_TYPE_NAMES, link_type + ) @staticmethod def air_mode_name(air_mode): - return name_or_number(HCI_Synchronous_Connection_Complete_Event.AIR_MODE_NAMES, air_mode) + return name_or_number( + HCI_Synchronous_Connection_Complete_Event.AIR_MODE_NAMES, air_mode + ) # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2), - ('transmission_interval', 1), - ('retransmission_window', 1), - ('rx_packet_length', 2), - ('tx_packet_length', 2) -]) +@HCI_Event.event( + [ + ('status', STATUS_SPEC), + ('connection_handle', 2), + ('transmission_interval', 1), + ('retransmission_window', 1), + ('rx_packet_length', 2), + ('tx_packet_length', 2), + ] +) class HCI_Synchronous_Connection_Changed_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.36 Synchronous Connection Changed Event @@ -3365,16 +5231,18 @@ class HCI_Synchronous_Connection_Changed_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('num_responses', 1), - ('bd_addr', Address.parse_address), - ('page_scan_repetition_mode', 1), - ('reserved', 1), - ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), - ('clock_offset', 2), - ('rssi', -1), - ('extended_inquiry_response', 240), -]) +@HCI_Event.event( + [ + ('num_responses', 1), + ('bd_addr', Address.parse_address), + ('page_scan_repetition_mode', 1), + ('reserved', 1), + ('class_of_device', {'size': 3, 'mapper': map_class_of_device}), + ('clock_offset', 2), + ('rssi', -1), + ('extended_inquiry_response', 240), + ] +) class HCI_Extended_Inquiry_Result_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.38 Extended Inquiry Result Event @@ -3382,10 +5250,7 @@ class HCI_Extended_Inquiry_Result_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('connection_handle', 2) -]) +@HCI_Event.event([('status', STATUS_SPEC), ('connection_handle', 2)]) class HCI_Encryption_Key_Refresh_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.39 Encryption Key Refresh Complete Event @@ -3393,9 +5258,7 @@ class HCI_Encryption_Key_Refresh_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address) -]) +@HCI_Event.event([('bd_addr', Address.parse_address)]) class HCI_IO_Capability_Request_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.40 IO Capability Request Event @@ -3403,12 +5266,17 @@ class HCI_IO_Capability_Request_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address), - ('io_capability', {'size': 1, 'mapper': HCI_Constant.io_capability_name}), - ('oob_data_present', 1), - ('authentication_requirements', {'size': 1, 'mapper': HCI_Constant.authentication_requirements_name}) -]) +@HCI_Event.event( + [ + ('bd_addr', Address.parse_address), + ('io_capability', {'size': 1, 'mapper': HCI_Constant.io_capability_name}), + ('oob_data_present', 1), + ( + 'authentication_requirements', + {'size': 1, 'mapper': HCI_Constant.authentication_requirements_name}, + ), + ] +) class HCI_IO_Capability_Response_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.41 IO Capability Response Event @@ -3416,10 +5284,7 @@ class HCI_IO_Capability_Response_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address), - ('numeric_value', 4) -]) +@HCI_Event.event([('bd_addr', Address.parse_address), ('numeric_value', 4)]) class HCI_User_Confirmation_Request_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.42 User Confirmation Request Event @@ -3427,9 +5292,7 @@ class HCI_User_Confirmation_Request_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address) -]) +@HCI_Event.event([('bd_addr', Address.parse_address)]) class HCI_User_Passkey_Request_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.43 User Passkey Request Event @@ -3437,10 +5300,7 @@ class HCI_User_Passkey_Request_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('status', STATUS_SPEC), - ('bd_addr', Address.parse_address) -]) +@HCI_Event.event([('status', STATUS_SPEC), ('bd_addr', Address.parse_address)]) class HCI_Simple_Pairing_Complete_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.45 Simple Pairing Complete Event @@ -3448,10 +5308,7 @@ class HCI_Simple_Pairing_Complete_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('connection_handle', 2), - ('link_supervision_timeout', 2) -]) +@HCI_Event.event([('connection_handle', 2), ('link_supervision_timeout', 2)]) class HCI_Link_Supervision_Timeout_Changed_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.46 Link Supervision Timeout Changed Event @@ -3459,10 +5316,15 @@ class HCI_Link_Supervision_Timeout_Changed_Event(HCI_Event): # ----------------------------------------------------------------------------- -@HCI_Event.event([ - ('bd_addr', Address.parse_address), - ('host_supported_features', 8) -]) +@HCI_Event.event([('bd_addr', Address.parse_address), ('passkey', 4)]) +class HCI_User_Passkey_Notification_Event(HCI_Event): + ''' + See Bluetooth spec @ 7.7.48 User Passkey Notification Event + ''' + + +# ----------------------------------------------------------------------------- +@HCI_Event.event([('bd_addr', Address.parse_address), ('host_supported_features', 8)]) class HCI_Remote_Host_Supported_Features_Notification_Event(HCI_Event): ''' See Bluetooth spec @ 7.7.50 Remote Host Supported Features Notification Event @@ -3470,10 +5332,11 @@ class HCI_Remote_Host_Supported_Features_Notification_Event(HCI_Event): # ----------------------------------------------------------------------------- -class HCI_AclDataPacket(HCI_Packet): +class HCI_AclDataPacket: ''' See Bluetooth spec @ 5.4.2 HCI ACL Data Packets ''' + hci_packet_type = HCI_ACL_DATA_PACKET @staticmethod @@ -3486,11 +5349,16 @@ class HCI_AclDataPacket(HCI_Packet): data = packet[5:] if len(data) != data_total_length: raise ValueError('invalid packet length') - return HCI_AclDataPacket(connection_handle, pb_flag, bc_flag, data_total_length, data) + return HCI_AclDataPacket( + connection_handle, pb_flag, bc_flag, data_total_length, data + ) def to_bytes(self): h = (self.pb_flag << 12) | (self.bc_flag << 14) | self.connection_handle - return struct.pack('<BHH', HCI_ACL_DATA_PACKET, h, self.data_total_length) + self.data + return ( + struct.pack('<BHH', HCI_ACL_DATA_PACKET, h, self.data_total_length) + + self.data + ) def __init__(self, connection_handle, pb_flag, bc_flag, data_total_length, data): self.connection_handle = connection_handle @@ -3503,20 +5371,29 @@ class HCI_AclDataPacket(HCI_Packet): return self.to_bytes() def __str__(self): - return f'{color("ACL", "blue")}: handle=0x{self.connection_handle:04x}, pb={self.pb_flag}, bc={self.bc_flag}, data_total_length={self.data_total_length}, data={self.data.hex()}' + return ( + f'{color("ACL", "blue")}: ' + f'handle=0x{self.connection_handle:04x}' + f'pb={self.pb_flag}, bc={self.bc_flag}, ' + f'data_total_length={self.data_total_length}, ' + f'data={self.data.hex()}' + ) # ----------------------------------------------------------------------------- class HCI_AclDataPacketAssembler: def __init__(self, callback): - self.callback = callback - self.current_data = None + self.callback = callback + self.current_data = None self.l2cap_pdu_length = 0 def feed_packet(self, packet): - if packet.pb_flag == HCI_ACL_PB_FIRST_NON_FLUSHABLE or packet.pb_flag == HCI_ACL_PB_FIRST_FLUSHABLE: - (l2cap_pdu_length,) = struct.unpack_from('<H', packet.data, 0) - self.current_data = packet.data + if packet.pb_flag in ( + HCI_ACL_PB_FIRST_NON_FLUSHABLE, + HCI_ACL_PB_FIRST_FLUSHABLE, + ): + (l2cap_pdu_length,) = struct.unpack_from('<H', packet.data, 0) + self.current_data = packet.data self.l2cap_pdu_length = l2cap_pdu_length elif packet.pb_flag == HCI_ACL_PB_CONTINUATION: if self.current_data is None: @@ -3530,11 +5407,11 @@ class HCI_AclDataPacketAssembler: self.callback(self.current_data) # Reset - self.current_data = None + self.current_data = None self.l2cap_pdu_length = 0 else: # Sanity check if len(self.current_data) > self.l2cap_pdu_length + 4: logger.warning('!!! ACL data exceeds L2CAP PDU') - self.current_data = None + self.current_data = None self.l2cap_pdu_length = 0 diff --git a/bumble/helpers.py b/bumble/helpers.py index 09b86ef..83c7c6d 100644 --- a/bumble/helpers.py +++ b/bumble/helpers.py @@ -16,10 +16,11 @@ # Imports # ----------------------------------------------------------------------------- import logging -from colors import color +from .colors import color +from .att import ATT_CID, ATT_PDU +from .smp import SMP_CID, SMP_Command from .core import name_or_number -from .gatt import ATT_PDU, ATT_CID from .l2cap import ( L2CAP_PDU, L2CAP_CONNECTION_REQUEST, @@ -27,20 +28,17 @@ from .l2cap import ( L2CAP_SIGNALING_CID, L2CAP_LE_SIGNALING_CID, L2CAP_Control_Frame, - L2CAP_Connection_Response + L2CAP_Connection_Response, ) from .hci import ( HCI_EVENT_PACKET, HCI_ACL_DATA_PACKET, HCI_DISCONNECTION_COMPLETE_EVENT, - HCI_AclDataPacketAssembler + HCI_AclDataPacketAssembler, ) from .rfcomm import RFCOMM_Frame, RFCOMM_PSM from .sdp import SDP_PDU, SDP_PSM -from .avdtp import ( - MessageAssembler as AVDTP_MessageAssembler, - AVDTP_PSM -) +from .avdtp import MessageAssembler as AVDTP_MessageAssembler, AVDTP_PSM # ----------------------------------------------------------------------------- # Logging @@ -51,8 +49,8 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- PSM_NAMES = { RFCOMM_PSM: 'RFCOMM', - SDP_PSM: 'SDP', - AVDTP_PSM: 'AVDTP' + SDP_PSM: 'SDP', + AVDTP_PSM: 'AVDTP' # TODO: add more PSM values } @@ -61,19 +59,23 @@ PSM_NAMES = { class PacketTracer: class AclStream: def __init__(self, analyzer): - self.analyzer = analyzer + self.analyzer = analyzer self.packet_assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu) - self.avdtp_assemblers = {} # AVDTP assemblers, by source_cid - self.psms = {} # PSM, by source_cid - self.peer = None # ACL stream in the other direction + self.avdtp_assemblers = {} # AVDTP assemblers, by source_cid + self.psms = {} # PSM, by source_cid + self.peer = None # ACL stream in the other direction + # pylint: disable=too-many-nested-blocks def on_acl_pdu(self, pdu): l2cap_pdu = L2CAP_PDU.from_bytes(pdu) if l2cap_pdu.cid == ATT_CID: att_pdu = ATT_PDU.from_bytes(l2cap_pdu.payload) self.analyzer.emit(att_pdu) - elif l2cap_pdu.cid == L2CAP_SIGNALING_CID or l2cap_pdu.cid == L2CAP_LE_SIGNALING_CID: + elif l2cap_pdu.cid == SMP_CID: + smp_command = SMP_Command.from_bytes(l2cap_pdu.payload) + self.analyzer.emit(smp_command) + elif l2cap_pdu.cid in (L2CAP_SIGNALING_CID, L2CAP_LE_SIGNALING_CID): control_frame = L2CAP_Control_Frame.from_bytes(l2cap_pdu.payload) self.analyzer.emit(control_frame) @@ -81,16 +83,26 @@ class PacketTracer: if control_frame.code == L2CAP_CONNECTION_REQUEST: self.psms[control_frame.source_cid] = control_frame.psm elif control_frame.code == L2CAP_CONNECTION_RESPONSE: - if control_frame.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL: + if ( + control_frame.result + == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL + ): if self.peer: if psm := self.peer.psms.get(control_frame.source_cid): # Found a pending connection self.psms[control_frame.destination_cid] = psm - # For AVDTP connections, create a packet assembler for each direction + # For AVDTP connections, create a packet assembler for + # each direction if psm == AVDTP_PSM: - self.avdtp_assemblers[control_frame.source_cid] = AVDTP_MessageAssembler(self.on_avdtp_message) - self.peer.avdtp_assemblers[control_frame.destination_cid] = AVDTP_MessageAssembler(self.peer.on_avdtp_message) + self.avdtp_assemblers[ + control_frame.source_cid + ] = AVDTP_MessageAssembler(self.on_avdtp_message) + self.peer.avdtp_assemblers[ + control_frame.destination_cid + ] = AVDTP_MessageAssembler( + self.peer.on_avdtp_message + ) else: # Try to find the PSM associated with this PDU @@ -102,31 +114,42 @@ class PacketTracer: rfcomm_frame = RFCOMM_Frame.from_bytes(l2cap_pdu.payload) self.analyzer.emit(rfcomm_frame) elif psm == AVDTP_PSM: - self.analyzer.emit(f'{color("L2CAP", "green")} [CID={l2cap_pdu.cid}, PSM=AVDTP]: {l2cap_pdu.payload.hex()}') + self.analyzer.emit( + f'{color("L2CAP", "green")} [CID={l2cap_pdu.cid}, ' + f'PSM=AVDTP]: {l2cap_pdu.payload.hex()}' + ) assembler = self.avdtp_assemblers.get(l2cap_pdu.cid) if assembler: assembler.on_pdu(l2cap_pdu.payload) else: psm_string = name_or_number(PSM_NAMES, psm) - self.analyzer.emit(f'{color("L2CAP", "green")} [CID={l2cap_pdu.cid}, PSM={psm_string}]: {l2cap_pdu.payload.hex()}') + self.analyzer.emit( + f'{color("L2CAP", "green")} [CID={l2cap_pdu.cid}, ' + f'PSM={psm_string}]: {l2cap_pdu.payload.hex()}' + ) else: self.analyzer.emit(l2cap_pdu) def on_avdtp_message(self, transaction_label, message): - self.analyzer.emit(f'{color("AVDTP", "green")} [{transaction_label}] {message}') + self.analyzer.emit( + f'{color("AVDTP", "green")} [{transaction_label}] {message}' + ) def feed_packet(self, packet): self.packet_assembler.feed_packet(packet) class Analyzer: def __init__(self, label, emit_message): - self.label = label + self.label = label self.emit_message = emit_message - self.acl_streams = {} # ACL streams, by connection handle - self.peer = None # Analyzer in the other direction + self.acl_streams = {} # ACL streams, by connection handle + self.peer = None # Analyzer in the other direction def start_acl_stream(self, connection_handle): - logger.info(f'[{self.label}] +++ Creating ACL stream for connection 0x{connection_handle:04X}') + logger.info( + f'[{self.label}] +++ Creating ACL stream for connection ' + f'0x{connection_handle:04X}' + ) stream = PacketTracer.AclStream(self) self.acl_streams[connection_handle] = stream @@ -139,7 +162,10 @@ class PacketTracer: def end_acl_stream(self, connection_handle): if connection_handle in self.acl_streams: - logger.info(f'[{self.label}] --- Removing ACL stream for connection 0x{connection_handle:04X}') + logger.info( + f'[{self.label}] --- Removing ACL stream for connection ' + f'0x{connection_handle:04X}' + ) del self.acl_streams[connection_handle] # Let the other forwarder know so it can cleanup its stream as well @@ -171,9 +197,13 @@ class PacketTracer: self, host_to_controller_label=color('HOST->CONTROLLER', 'blue'), controller_to_host_label=color('CONTROLLER->HOST', 'cyan'), - emit_message=logger.info + emit_message=logger.info, ): - self.host_to_controller_analyzer = PacketTracer.Analyzer(host_to_controller_label, emit_message) - self.controller_to_host_analyzer = PacketTracer.Analyzer(controller_to_host_label, emit_message) + self.host_to_controller_analyzer = PacketTracer.Analyzer( + host_to_controller_label, emit_message + ) + self.controller_to_host_analyzer = PacketTracer.Analyzer( + controller_to_host_label, emit_message + ) self.host_to_controller_analyzer.peer = self.controller_to_host_analyzer self.controller_to_host_analyzer.peer = self.host_to_controller_analyzer diff --git a/bumble/hfp.py b/bumble/hfp.py index 6eeb0d9..7bb9f08 100644 --- a/bumble/hfp.py +++ b/bumble/hfp.py @@ -18,7 +18,8 @@ import logging import asyncio import collections -from colors import color + +from .colors import color # ----------------------------------------------------------------------------- @@ -34,16 +35,16 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- class HfpProtocol: def __init__(self, dlc): - self.dlc = dlc - self.buffer = '' - self.lines = collections.deque() + self.dlc = dlc + self.buffer = '' + self.lines = collections.deque() self.lines_available = asyncio.Event() dlc.sink = self.feed def feed(self, data): # Convert the data to a string if needed - if type(data) == bytes: + if isinstance(data, bytes): data = data.decode('utf-8') logger.debug(f'<<< Data received: {data}') @@ -52,7 +53,7 @@ class HfpProtocol: self.buffer += data while (separator := self.buffer.find('\r')) >= 0: line = self.buffer[:separator].strip() - self.buffer = self.buffer[separator + 1:] + self.buffer = self.buffer[separator + 1 :] if len(line) > 0: self.on_line(line) @@ -79,16 +80,16 @@ class HfpProtocol: async def initialize_service(self): # Perform Service Level Connection Initialization self.send_command_line('AT+BRSF=2072') # Retrieve Supported Features - line = await(self.next_line()) - line = await(self.next_line()) + await (self.next_line()) + await (self.next_line()) self.send_command_line('AT+CIND=?') - line = await(self.next_line()) - line = await(self.next_line()) + await (self.next_line()) + await (self.next_line()) self.send_command_line('AT+CIND?') - line = await(self.next_line()) - line = await(self.next_line()) + await (self.next_line()) + await (self.next_line()) self.send_command_line('AT+CMER=3,0,0,1') - line = await(self.next_line()) + await (self.next_line()) diff --git a/bumble/host.py b/bumble/host.py index 4057402..afde2ee 100644 --- a/bumble/host.py +++ b/bumble/host.py @@ -16,16 +16,62 @@ # Imports # ----------------------------------------------------------------------------- import asyncio +import collections import logging -from pyee import EventEmitter -from colors import color +import struct + +from bumble.colors import color +from bumble.l2cap import L2CAP_PDU +from bumble.snoop import Snooper + +from typing import Optional + +from .hci import ( + Address, + HCI_ACL_DATA_PACKET, + HCI_COMMAND_COMPLETE_EVENT, + HCI_COMMAND_PACKET, + HCI_EVENT_PACKET, + HCI_LE_READ_BUFFER_SIZE_COMMAND, + HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND, + HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND, + HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND, + HCI_READ_BUFFER_SIZE_COMMAND, + HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND, + HCI_RESET_COMMAND, + HCI_SUCCESS, + HCI_SUPPORTED_COMMANDS_FLAGS, + HCI_VERSION_BLUETOOTH_CORE_4_0, + HCI_AclDataPacket, + HCI_AclDataPacketAssembler, + HCI_Constant, + HCI_Error, + HCI_LE_Long_Term_Key_Request_Negative_Reply_Command, + HCI_LE_Long_Term_Key_Request_Reply_Command, + HCI_LE_Read_Buffer_Size_Command, + HCI_LE_Read_Local_Supported_Features_Command, + HCI_LE_Read_Suggested_Default_Data_Length_Command, + HCI_LE_Remote_Connection_Parameter_Request_Reply_Command, + HCI_LE_Set_Event_Mask_Command, + HCI_LE_Write_Suggested_Default_Data_Length_Command, + HCI_Link_Key_Request_Negative_Reply_Command, + HCI_Link_Key_Request_Reply_Command, + HCI_Packet, + HCI_Read_Buffer_Size_Command, + HCI_Read_Local_Supported_Commands_Command, + HCI_Read_Local_Version_Information_Command, + HCI_Reset_Command, + HCI_Set_Event_Mask_Command, +) +from .core import ( + BT_BR_EDR_TRANSPORT, + BT_CENTRAL_ROLE, + BT_LE_TRANSPORT, + ConnectionPHY, + ConnectionParameters, +) +from .utils import AbortableEventEmitter -from .hci import * -from .l2cap import * -from .att import * -from .gatt import * -from .smp import * -from .core import ConnectionParameters # ----------------------------------------------------------------------------- # Logging @@ -36,56 +82,60 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off + HOST_DEFAULT_HC_LE_ACL_DATA_PACKET_LENGTH = 27 HOST_HC_TOTAL_NUM_LE_ACL_DATA_PACKETS = 1 HOST_DEFAULT_HC_ACL_DATA_PACKET_LENGTH = 27 HOST_HC_TOTAL_NUM_ACL_DATA_PACKETS = 1 +# fmt: on + # ----------------------------------------------------------------------------- class Connection: - def __init__(self, host, handle, role, peer_address): - self.host = host - self.handle = handle - self.role = role - self.peer_address = peer_address - self.assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu) + def __init__(self, host, handle, peer_address, transport): + self.host = host + self.handle = handle + self.peer_address = peer_address + self.assembler = HCI_AclDataPacketAssembler(self.on_acl_pdu) + self.transport = transport def on_hci_acl_data_packet(self, packet): self.assembler.feed_packet(packet) def on_acl_pdu(self, pdu): l2cap_pdu = L2CAP_PDU.from_bytes(pdu) - - if l2cap_pdu.cid == ATT_CID: - self.host.on_gatt_pdu(self, l2cap_pdu.payload) - elif l2cap_pdu.cid == SMP_CID: - self.host.on_smp_pdu(self, l2cap_pdu.payload) - else: - self.host.on_l2cap_pdu(self, l2cap_pdu.cid, l2cap_pdu.payload) + self.host.on_l2cap_pdu(self, l2cap_pdu.cid, l2cap_pdu.payload) # ----------------------------------------------------------------------------- -class Host(EventEmitter): - def __init__(self, controller_source = None, controller_sink = None): +class Host(AbortableEventEmitter): + def __init__(self, controller_source=None, controller_sink=None): super().__init__() - self.hci_sink = None - self.ready = False # True when we can accept incoming packets - self.connections = {} # Connections, by connection handle - self.pending_command = None - self.pending_response = None - self.hc_le_acl_data_packet_length = HOST_DEFAULT_HC_LE_ACL_DATA_PACKET_LENGTH + self.hci_sink = None + self.ready = False # True when we can accept incoming packets + self.reset_done = False + self.connections = {} # Connections, by connection handle + self.pending_command = None + self.pending_response = None + self.hc_le_acl_data_packet_length = HOST_DEFAULT_HC_LE_ACL_DATA_PACKET_LENGTH self.hc_total_num_le_acl_data_packets = HOST_HC_TOTAL_NUM_LE_ACL_DATA_PACKETS - self.hc_acl_data_packet_length = HOST_DEFAULT_HC_ACL_DATA_PACKET_LENGTH - self.hc_total_num_acl_data_packets = HOST_HC_TOTAL_NUM_ACL_DATA_PACKETS - self.acl_packet_queue = collections.deque() - self.acl_packets_in_flight = 0 - self.local_supported_commands = bytes(64) - self.command_semaphore = asyncio.Semaphore(1) - self.long_term_key_provider = None - self.link_key_provider = None - self.pairing_io_capability_provider = None # Classic only + self.hc_acl_data_packet_length = HOST_DEFAULT_HC_ACL_DATA_PACKET_LENGTH + self.hc_total_num_acl_data_packets = HOST_HC_TOTAL_NUM_ACL_DATA_PACKETS + self.acl_packet_queue = collections.deque() + self.acl_packets_in_flight = 0 + self.local_version = None + self.local_supported_commands = bytes(64) + self.local_le_features = 0 + self.suggested_max_tx_octets = 251 # Max allowed + self.suggested_max_tx_time = 2120 # Max allowed + self.command_semaphore = asyncio.Semaphore(1) + self.long_term_key_provider = None + self.link_key_provider = None + self.pairing_io_capability_provider = None # Classic only + self.snooper = None # Connect to the source and sink if specified if controller_source: @@ -93,38 +143,140 @@ class Host(EventEmitter): if controller_sink: self.set_packet_sink(controller_sink) + def find_connection_by_bd_addr( + self, + bd_addr: Address, + transport: Optional[int] = None, + check_address_type: bool = False, + ) -> Optional[Connection]: + for connection in self.connections.values(): + if connection.peer_address.to_bytes() == bd_addr.to_bytes(): + if ( + check_address_type + and connection.peer_address.address_type != bd_addr.address_type + ): + continue + if transport is None or connection.transport == transport: + return connection + + return None + + async def flush(self) -> None: + # Make sure no command is pending + await self.command_semaphore.acquire() + + # Flush current host state, then release command semaphore + self.emit('flush') + self.command_semaphore.release() + async def reset(self): - await self.send_command(HCI_Reset_Command()) + if self.ready: + self.ready = False + await self.flush() + + await self.send_command(HCI_Reset_Command(), check_result=True) self.ready = True - response = await self.send_command(HCI_Read_Local_Supported_Commands_Command()) - if response.return_parameters.status != HCI_SUCCESS: - raise ProtocolError(response.return_parameters.status, 'hci') + response = await self.send_command( + HCI_Read_Local_Supported_Commands_Command(), check_result=True + ) self.local_supported_commands = response.return_parameters.supported_commands - await self.send_command(HCI_Set_Event_Mask_Command(event_mask = bytes.fromhex('FFFFFFFFFFFFFFFF'))) - await self.send_command(HCI_LE_Set_Event_Mask_Command(le_event_mask = bytes.fromhex('FFFFF00000000000'))) - await self.send_command(HCI_Read_Local_Version_Information_Command()) - await self.send_command(HCI_Write_LE_Host_Support_Command(le_supported_host = 1, simultaneous_le_host = 0)) + if self.supports_command(HCI_LE_READ_LOCAL_SUPPORTED_FEATURES_COMMAND): + response = await self.send_command( + HCI_LE_Read_Local_Supported_Features_Command(), check_result=True + ) + self.local_le_features = struct.unpack( + '<Q', response.return_parameters.le_features + )[0] - response = await self.send_command(HCI_LE_Read_Buffer_Size_Command()) - if response.return_parameters.status == HCI_SUCCESS: - self.hc_le_acl_data_packet_length = response.return_parameters.hc_le_acl_data_packet_length - self.hc_total_num_le_acl_data_packets = response.return_parameters.hc_total_num_le_acl_data_packets - logger.debug(f'HCI LE ACL flow control: hc_le_acl_data_packet_length={response.return_parameters.hc_le_acl_data_packet_length}, hc_total_num_le_acl_data_packets={response.return_parameters.hc_total_num_le_acl_data_packets}') + if self.supports_command(HCI_READ_LOCAL_VERSION_INFORMATION_COMMAND): + response = await self.send_command( + HCI_Read_Local_Version_Information_Command(), check_result=True + ) + self.local_version = response.return_parameters + + await self.send_command( + HCI_Set_Event_Mask_Command(event_mask=bytes.fromhex('FFFFFFFFFFFFFF3F')) + ) + + if ( + self.local_version is not None + and self.local_version.hci_version <= HCI_VERSION_BLUETOOTH_CORE_4_0 + ): + # Some older controllers don't like event masks with bits they don't + # understand + le_event_mask = bytes.fromhex('1F00000000000000') else: - logger.warn(f'HCI_LE_Read_Buffer_Size_Command failed: {response.return_parameters.status}') - if response.return_parameters.hc_le_acl_data_packet_length == 0 or response.return_parameters.hc_total_num_le_acl_data_packets == 0: - # Read the non-LE-specific values - response = await self.send_command(HCI_Read_Buffer_Size_Command()) - if response.return_parameters.status == HCI_SUCCESS: - self.hc_acl_data_packet_length = response.return_parameters.hc_le_acl_data_packet_length - self.hc_le_acl_data_packet_length = self.hc_le_acl_data_packet_length or self.hc_acl_data_packet_length - self.hc_total_num_acl_data_packets = response.return_parameters.hc_total_num_le_acl_data_packets - self.hc_total_num_le_acl_data_packets = self.hc_total_num_le_acl_data_packets or self.hc_total_num_acl_data_packets - logger.debug(f'HCI LE ACL flow control: hc_le_acl_data_packet_length={self.hc_le_acl_data_packet_length}, hc_total_num_le_acl_data_packets={self.hc_total_num_le_acl_data_packets}') - else: - logger.warn(f'HCI_Read_Buffer_Size_Command failed: {response.return_parameters.status}') + le_event_mask = bytes.fromhex('FFFFF00000000000') + + await self.send_command( + HCI_LE_Set_Event_Mask_Command(le_event_mask=le_event_mask) + ) + + if self.supports_command(HCI_READ_BUFFER_SIZE_COMMAND): + response = await self.send_command( + HCI_Read_Buffer_Size_Command(), check_result=True + ) + self.hc_acl_data_packet_length = ( + response.return_parameters.hc_acl_data_packet_length + ) + self.hc_total_num_acl_data_packets = ( + response.return_parameters.hc_total_num_acl_data_packets + ) + + logger.debug( + 'HCI ACL flow control: ' + f'hc_acl_data_packet_length={self.hc_acl_data_packet_length},' + f'hc_total_num_acl_data_packets={self.hc_total_num_acl_data_packets}' + ) + + if self.supports_command(HCI_LE_READ_BUFFER_SIZE_COMMAND): + response = await self.send_command( + HCI_LE_Read_Buffer_Size_Command(), check_result=True + ) + self.hc_le_acl_data_packet_length = ( + response.return_parameters.hc_le_acl_data_packet_length + ) + self.hc_total_num_le_acl_data_packets = ( + response.return_parameters.hc_total_num_le_acl_data_packets + ) + + logger.debug( + 'HCI LE ACL flow control: ' + f'hc_le_acl_data_packet_length={self.hc_le_acl_data_packet_length},' + 'hc_total_num_le_acl_data_packets=' + f'{self.hc_total_num_le_acl_data_packets}' + ) + + if ( + response.return_parameters.hc_le_acl_data_packet_length == 0 + or response.return_parameters.hc_total_num_le_acl_data_packets == 0 + ): + # LE and Classic share the same values + self.hc_le_acl_data_packet_length = self.hc_acl_data_packet_length + self.hc_total_num_le_acl_data_packets = ( + self.hc_total_num_acl_data_packets + ) + + if self.supports_command( + HCI_LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND + ) and self.supports_command(HCI_LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH_COMMAND): + response = await self.send_command( + HCI_LE_Read_Suggested_Default_Data_Length_Command() + ) + suggested_max_tx_octets = response.return_parameters.suggested_max_tx_octets + suggested_max_tx_time = response.return_parameters.suggested_max_tx_time + if ( + suggested_max_tx_octets != self.suggested_max_tx_octets + or suggested_max_tx_time != self.suggested_max_tx_time + ): + await self.send_command( + HCI_LE_Write_Suggested_Default_Data_Length_Command( + suggested_max_tx_octets=self.suggested_max_tx_octets, + suggested_max_tx_time=self.suggested_max_tx_time, + ) + ) self.reset_done = True @@ -142,15 +294,18 @@ class Host(EventEmitter): self.hci_sink = sink def send_hci_packet(self, packet): + if self.snooper: + self.snooper.snoop(bytes(packet), Snooper.Direction.HOST_TO_CONTROLLER) + self.hci_sink.on_packet(packet.to_bytes()) - async def send_command(self, command): + async def send_command(self, command, check_result=False): logger.debug(f'{color("### HOST -> CONTROLLER", "blue")}: {command}') # Wait until we can send (only one pending command at a time) async with self.command_semaphore: - assert(self.pending_command is None) - assert(self.pending_response is None) + assert self.pending_command is None + assert self.pending_response is None # Create a future value to hold the eventual response self.pending_response = asyncio.get_running_loop().create_future() @@ -159,11 +314,29 @@ class Host(EventEmitter): try: self.send_hci_packet(command) response = await self.pending_response - # TODO: check error values + + # Check the return parameters if required + if check_result: + if isinstance(response.return_parameters, int): + status = response.return_parameters + elif isinstance(response.return_parameters, bytes): + # return parameters first field is a one byte status code + status = response.return_parameters[0] + else: + status = response.return_parameters.status + + if status != HCI_SUCCESS: + logger.warning( + f'{command.name} failed ({HCI_Constant.error_name(status)})' + ) + raise HCI_Error(status) + return response except Exception as error: - logger.warning(f'{color("!!! Exception while sending HCI packet:", "red")} {error}') - # raise error + logger.warning( + f'{color("!!! Exception while sending HCI packet:", "red")} {error}' + ) + raise error finally: self.pending_command = None self.pending_response = None @@ -183,15 +356,18 @@ class Host(EventEmitter): offset = 0 pb_flag = 0 while bytes_remaining: + # TODO: support different LE/Classic lengths data_total_length = min(bytes_remaining, self.hc_le_acl_data_packet_length) acl_packet = HCI_AclDataPacket( - connection_handle = connection_handle, - pb_flag = pb_flag, - bc_flag = 0, - data_total_length = data_total_length, - data = l2cap_pdu[offset:offset + data_total_length] + connection_handle=connection_handle, + pb_flag=pb_flag, + bc_flag=0, + data_total_length=data_total_length, + data=l2cap_pdu[offset : offset + data_total_length], + ) + logger.debug( + f'{color("### HOST -> CONTROLLER", "blue")}: (CID={cid}) {acl_packet}' ) - logger.debug(f'{color("### HOST -> CONTROLLER", "blue")}: (CID={cid}) {acl_packet}') self.queue_acl_packet(acl_packet) pb_flag = 1 offset += data_total_length @@ -202,22 +378,63 @@ class Host(EventEmitter): self.check_acl_packet_queue() if len(self.acl_packet_queue): - logger.debug(f'{self.acl_packets_in_flight} ACL packets in flight, {len(self.acl_packet_queue)} in queue') + logger.debug( + f'{self.acl_packets_in_flight} ACL packets in flight, ' + f'{len(self.acl_packet_queue)} in queue' + ) def check_acl_packet_queue(self): - # Send all we can - while len(self.acl_packet_queue) > 0 and self.acl_packets_in_flight < self.hc_total_num_le_acl_data_packets: + # Send all we can (TODO: support different LE/Classic limits) + while ( + len(self.acl_packet_queue) > 0 + and self.acl_packets_in_flight < self.hc_total_num_le_acl_data_packets + ): packet = self.acl_packet_queue.pop() self.send_hci_packet(packet) self.acl_packets_in_flight += 1 + def supports_command(self, command): + # Find the support flag position for this command + for octet, flags in enumerate(HCI_SUPPORTED_COMMANDS_FLAGS): + for flag_position, value in enumerate(flags): + if value == command: + # Check if the flag is set + if octet < len(self.local_supported_commands) and flag_position < 8: + return ( + self.local_supported_commands[octet] & (1 << flag_position) + ) != 0 + + return False + + @property + def supported_commands(self): + commands = [] + for octet, flags in enumerate(self.local_supported_commands): + if octet < len(HCI_SUPPORTED_COMMANDS_FLAGS): + for flag in range(8): + if flags & (1 << flag) != 0: + command = HCI_SUPPORTED_COMMANDS_FLAGS[octet][flag] + if command is not None: + commands.append(command) + + return commands + + def supports_le_feature(self, feature): + return (self.local_le_features & (1 << feature)) != 0 + + @property + def supported_le_features(self): + return [ + feature for feature in range(64) if self.local_le_features & (1 << feature) + ] + # Packet Sink protocol (packets coming from the controller via HCI) def on_packet(self, packet): hci_packet = HCI_Packet.from_bytes(packet) if self.ready or ( - hci_packet.hci_packet_type == HCI_EVENT_PACKET and - hci_packet.event_code == HCI_COMMAND_COMPLETE_EVENT and - hci_packet.command_opcode == HCI_RESET_COMMAND + hci_packet.hci_packet_type == HCI_EVENT_PACKET + and hci_packet.event_code == HCI_COMMAND_COMPLETE_EVENT + and hci_packet.command_opcode == HCI_RESET_COMMAND ): self.on_hci_packet(hci_packet) else: @@ -226,6 +443,9 @@ class Host(EventEmitter): def on_hci_packet(self, packet): logger.debug(f'{color("### CONTROLLER -> HOST", "green")}: {packet}') + if self.snooper: + self.snooper.snoop(bytes(packet), Snooper.Direction.CONTROLLER_TO_HOST) + # If the packet is a command, invoke the handler for this packet if packet.hci_packet_type == HCI_COMMAND_PACKET: self.on_hci_command_packet(packet) @@ -249,12 +469,6 @@ class Host(EventEmitter): if connection := self.connections.get(packet.connection_handle): connection.on_hci_acl_data_packet(packet) - def on_gatt_pdu(self, connection, pdu): - self.emit('gatt_pdu', connection.handle, pdu) - - def on_smp_pdu(self, connection, pdu): - self.emit('smp_pdu', connection.handle, pdu) - def on_l2cap_pdu(self, connection, cid, pdu): self.emit('l2cap_pdu', connection.handle, cid, pdu) @@ -262,7 +476,11 @@ class Host(EventEmitter): if self.pending_response: # Check that it is what we were expecting if self.pending_command.op_code != event.command_opcode: - logger.warning(f'!!! command result mismatch, expected 0x{self.pending_command.op_code:X} but got 0x{event.command_opcode:X}') + logger.warning( + '!!! command result mismatch, expected ' + f'0x{self.pending_command.op_code:X} but got ' + f'0x{event.command_opcode:X}' + ) self.pending_response.set_result(event) else: @@ -276,10 +494,12 @@ class Host(EventEmitter): def on_hci_command_complete_event(self, event): if event.command_opcode == 0: - # This is used just for the Num_HCI_Command_Packets field, not related to an actual command + # This is used just for the Num_HCI_Command_Packets field, not related to + # an actual command logger.debug('no-command event') - else: - return self.on_command_processed(event) + return None + + return self.on_command_processed(event) def on_hci_command_status_event(self, event): return self.on_command_processed(event) @@ -290,51 +510,64 @@ class Host(EventEmitter): self.acl_packets_in_flight -= total_packets self.check_acl_packet_queue() else: - logger.warning(color(f'!!! {total_packets} completed but only {self.acl_packets_in_flight} in flight')) + logger.warning( + color( + '!!! {total_packets} completed but only ' + f'{self.acl_packets_in_flight} in flight' + ) + ) self.acl_packets_in_flight = 0 # Classic only def on_hci_connection_request_event(self, event): - # For now, just accept everything - # TODO: delegate the decision - self.send_command_sync( - HCI_Accept_Connection_Request_Command( - bd_addr = event.bd_addr, - role = 0x01 # Remain the peripheral - ) + # Notify the listeners + self.emit( + 'connection_request', + event.bd_addr, + event.class_of_device, + event.link_type, ) def on_hci_le_connection_complete_event(self, event): # Check if this is a cancellation if event.status == HCI_SUCCESS: # Create/update the connection - logger.debug(f'### CONNECTION: [0x{event.connection_handle:04X}] {event.peer_address} as {HCI_Constant.role_name(event.role)}') + logger.debug( + f'### LE CONNECTION: [0x{event.connection_handle:04X}] ' + f'{event.peer_address} as {HCI_Constant.role_name(event.role)}' + ) connection = self.connections.get(event.connection_handle) if connection is None: - connection = Connection(self, event.connection_handle, event.role, event.peer_address) + connection = Connection( + self, + event.connection_handle, + event.peer_address, + BT_LE_TRANSPORT, + ) self.connections[event.connection_handle] = connection # Notify the client connection_parameters = ConnectionParameters( - event.conn_interval, - event.conn_latency, - event.supervision_timeout + event.connection_interval, + event.peripheral_latency, + event.supervision_timeout, ) self.emit( 'connection', event.connection_handle, BT_LE_TRANSPORT, event.peer_address, - None, event.role, - connection_parameters + connection_parameters, ) else: logger.debug(f'### CONNECTION FAILED: {event.status}') # Notify the listeners - self.emit('connection_failure', event.status) + self.emit( + 'connection_failure', BT_LE_TRANSPORT, event.peer_address, event.status + ) def on_hci_le_enhanced_connection_complete_event(self, event): # Just use the same implementation as for the non-enhanced event for now @@ -343,11 +576,19 @@ class Host(EventEmitter): def on_hci_connection_complete_event(self, event): if event.status == HCI_SUCCESS: # Create/update the connection - logger.debug(f'### BR/EDR CONNECTION: [0x{event.connection_handle:04X}] {event.bd_addr}') + logger.debug( + f'### BR/EDR CONNECTION: [0x{event.connection_handle:04X}] ' + f'{event.bd_addr}' + ) connection = self.connections.get(event.connection_handle) if connection is None: - connection = Connection(self, event.connection_handle, BT_CENTRAL_ROLE, event.bd_addr) + connection = Connection( + self, + event.connection_handle, + event.bd_addr, + BT_BR_EDR_TRANSPORT, + ) self.connections[event.connection_handle] = connection # Notify the client @@ -357,14 +598,15 @@ class Host(EventEmitter): BT_BR_EDR_TRANSPORT, event.bd_addr, None, - BT_CENTRAL_ROLE, - None + None, ) else: logger.debug(f'### BR/EDR CONNECTION FAILED: {event.status}') # Notify the client - self.emit('connection_failure', event.connection_handle, event.status) + self.emit( + 'connection_failure', BT_BR_EDR_TRANSPORT, event.bd_addr, event.status + ) def on_hci_disconnection_complete_event(self, event): # Find the connection @@ -373,7 +615,11 @@ class Host(EventEmitter): return if event.status == HCI_SUCCESS: - logger.debug(f'### DISCONNECTION: [0x{event.connection_handle:04X}] {connection.peer_address} as {HCI_Constant.role_name(connection.role)}, reason={event.reason}') + logger.debug( + f'### DISCONNECTION: [0x{event.connection_handle:04X}] ' + f'{connection.peer_address} ' + f'reason={event.reason}' + ) del self.connections[event.connection_handle] # Notify the listeners @@ -382,7 +628,7 @@ class Host(EventEmitter): logger.debug(f'### DISCONNECTION FAILED: {event.status}') # Notify the listeners - self.emit('disconnection_failure', event.status) + self.emit('disconnection_failure', event.connection_handle, event.status) def on_hci_le_connection_update_complete_event(self, event): if (connection := self.connections.get(event.connection_handle)) is None: @@ -392,13 +638,17 @@ class Host(EventEmitter): # Notify the client if event.status == HCI_SUCCESS: connection_parameters = ConnectionParameters( - event.conn_interval, - event.conn_latency, - event.supervision_timeout + event.connection_interval, + event.peripheral_latency, + event.supervision_timeout, + ) + self.emit( + 'connection_parameters_update', connection.handle, connection_parameters ) - self.emit('connection_parameters_update', connection.handle, connection_parameters) else: - self.emit('connection_parameters_update_failure', connection.handle, event.status) + self.emit( + 'connection_parameters_update_failure', connection.handle, event.status + ) def on_hci_le_phy_update_complete_event(self, event): if (connection := self.connections.get(event.connection_handle)) is None: @@ -414,13 +664,10 @@ class Host(EventEmitter): def on_hci_le_advertising_report_event(self, event): for report in event.reports: - self.emit( - 'advertising_report', - report.address, - report.data, - report.rssi, - report.event_type - ) + self.emit('advertising_report', report) + + def on_hci_le_extended_advertising_report_event(self, event): + self.on_hci_le_advertising_report_event(event) def on_hci_le_remote_connection_parameter_request_event(self, event): if event.connection_handle not in self.connections: @@ -431,13 +678,13 @@ class Host(EventEmitter): # TODO: delegate the decision self.send_command_sync( HCI_LE_Remote_Connection_Parameter_Request_Reply_Command( - connection_handle = event.connection_handle, - interval_min = event.interval_min, - interval_max = event.interval_max, - latency = event.latency, - timeout = event.timeout, - minimum_ce_length = 0, - maximum_ce_length = 0 + connection_handle=event.connection_handle, + interval_min=event.interval_min, + interval_max=event.interval_max, + max_latency=event.max_latency, + timeout=event.timeout, + min_ce_length=0, + max_ce_length=0, ) ) @@ -451,19 +698,23 @@ class Host(EventEmitter): logger.debug('no long term key provider') long_term_key = None else: - long_term_key = await self.long_term_key_provider( - connection.handle, - event.random_number, - event.encryption_diversifier + long_term_key = await self.abort_on( + 'flush', + # pylint: disable-next=not-callable + self.long_term_key_provider( + connection.handle, + event.random_number, + event.encryption_diversifier, + ), ) if long_term_key: response = HCI_LE_Long_Term_Key_Request_Reply_Command( - connection_handle = event.connection_handle, - long_term_key = long_term_key + connection_handle=event.connection_handle, + long_term_key=long_term_key, ) else: response = HCI_LE_Long_Term_Key_Request_Negative_Reply_Command( - connection_handle = event.connection_handle + connection_handle=event.connection_handle ) await self.send_command(response) @@ -478,10 +729,17 @@ class Host(EventEmitter): def on_hci_role_change_event(self, event): if event.status == HCI_SUCCESS: - logger.debug(f'role change for {event.bd_addr}: {HCI_Constant.role_name(event.new_role)}') - # TODO: lookup the connection and update the role + logger.debug( + f'role change for {event.bd_addr}: ' + f'{HCI_Constant.role_name(event.new_role)}' + ) + self.emit('role_change', event.bd_addr, event.new_role) else: - logger.debug(f'role change for {event.bd_addr} failed: {HCI_Constant.error_name(event.status)}') + logger.debug( + f'role change for {event.bd_addr} failed: ' + f'{HCI_Constant.error_name(event.status)}' + ) + self.emit('role_change_failure', event.bd_addr, event.status) def on_hci_le_data_length_change_event(self, event): self.emit( @@ -490,7 +748,7 @@ class Host(EventEmitter): event.max_tx_octets, event.max_tx_time, event.max_rx_octets, - event.max_rx_time + event.max_rx_time, ) def on_hci_authentication_complete_event(self, event): @@ -498,21 +756,35 @@ class Host(EventEmitter): if event.status == HCI_SUCCESS: self.emit('connection_authentication', event.connection_handle) else: - self.emit('connection_authentication_failure', event.connection_handle, event.status) + self.emit( + 'connection_authentication_failure', + event.connection_handle, + event.status, + ) def on_hci_encryption_change_event(self, event): # Notify the client if event.status == HCI_SUCCESS: - self.emit('connection_encryption_change', event.connection_handle, event.encryption_enabled) + self.emit( + 'connection_encryption_change', + event.connection_handle, + event.encryption_enabled, + ) else: - self.emit('connection_encryption_failure', event.connection_handle, event.status) + self.emit( + 'connection_encryption_failure', event.connection_handle, event.status + ) def on_hci_encryption_key_refresh_complete_event(self, event): # Notify the client if event.status == HCI_SUCCESS: self.emit('connection_encryption_key_refresh', event.connection_handle) else: - self.emit('connection_encryption_key_refresh_failure', event.connection_handle, event.status) + self.emit( + 'connection_encryption_key_refresh_failure', + event.connection_handle, + event.status, + ) def on_hci_link_supervision_timeout_changed_event(self, event): pass @@ -524,20 +796,20 @@ class Host(EventEmitter): pass def on_hci_link_key_notification_event(self, event): - logger.debug(f'link key for {event.bd_addr}: {event.link_key.hex()}, type={HCI_Constant.link_key_type_name(event.key_type)}') + logger.debug( + f'link key for {event.bd_addr}: {event.link_key.hex()}, ' + f'type={HCI_Constant.link_key_type_name(event.key_type)}' + ) self.emit('link_key', event.bd_addr, event.link_key, event.key_type) def on_hci_simple_pairing_complete_event(self, event): - logger.debug(f'simple pairing complete for {event.bd_addr}: status={HCI_Constant.status_name(event.status)}') + logger.debug( + f'simple pairing complete for {event.bd_addr}: ' + f'status={HCI_Constant.status_name(event.status)}' + ) def on_hci_pin_code_request_event(self, event): - # For now, just refuse all requests - # TODO: delegate the decision - self.send_command_sync( - HCI_PIN_Code_Request_Negative_Reply_Command( - bd_addr = event.bd_addr - ) - ) + self.emit('pin_code_request', event.bd_addr) def on_hci_link_key_request_event(self, event): async def send_link_key(): @@ -545,15 +817,18 @@ class Host(EventEmitter): logger.debug('no link key provider') link_key = None else: - link_key = await self.link_key_provider(event.bd_addr) + link_key = await self.abort_on( + 'flush', + # pylint: disable-next=not-callable + self.link_key_provider(event.bd_addr), + ) if link_key: response = HCI_Link_Key_Request_Reply_Command( - bd_addr = event.bd_addr, - link_key = link_key + bd_addr=event.bd_addr, link_key=link_key ) else: response = HCI_Link_Key_Request_Negative_Reply_Command( - bd_addr = event.bd_addr + bd_addr=event.bd_addr ) await self.send_command(response) @@ -564,15 +839,29 @@ class Host(EventEmitter): self.emit('authentication_io_capability_request', event.bd_addr) def on_hci_io_capability_response_event(self, event): - pass + self.emit( + 'authentication_io_capability_response', + event.bd_addr, + event.io_capability, + event.authentication_requirements, + ) def on_hci_user_confirmation_request_event(self, event): - self.emit('authentication_user_confirmation_request', event.bd_addr, event.numeric_value) + self.emit( + 'authentication_user_confirmation_request', + event.bd_addr, + event.numeric_value, + ) def on_hci_user_passkey_request_event(self, event): self.emit('authentication_user_passkey_request', event.bd_addr) - def on_hci_inquiry_complete_event(self, event): + def on_hci_user_passkey_notification_event(self, event): + self.emit( + 'authentication_user_passkey_notification', event.bd_addr, event.passkey + ) + + def on_hci_inquiry_complete_event(self, _event): self.emit('inquiry_complete') def on_hci_inquiry_result_with_rssi_event(self, event): @@ -582,7 +871,7 @@ class Host(EventEmitter): response.bd_addr, response.class_of_device, b'', - response.rssi + response.rssi, ) def on_hci_extended_inquiry_result_event(self, event): @@ -591,7 +880,7 @@ class Host(EventEmitter): event.bd_addr, event.class_of_device, event.extended_inquiry_response, - event.rssi + event.rssi, ) def on_hci_remote_name_request_complete_event(self, event): @@ -599,3 +888,10 @@ class Host(EventEmitter): self.emit('remote_name_failure', event.bd_addr, event.status) else: self.emit('remote_name', event.bd_addr, event.remote_name) + + def on_hci_remote_host_supported_features_notification_event(self, event): + self.emit( + 'remote_host_supported_features', + event.bd_addr, + event.host_supported_features, + ) diff --git a/bumble/keys.py b/bumble/keys.py index f51cfe6..a30e753 100644 --- a/bumble/keys.py +++ b/bumble/keys.py @@ -20,13 +20,19 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations +import asyncio import logging import os import json -from colors import color +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple +from .colors import color from .hci import Address +if TYPE_CHECKING: + from .device import Device + # ----------------------------------------------------------------------------- # Logging @@ -38,10 +44,10 @@ logger = logging.getLogger(__name__) class PairingKeys: class Key: def __init__(self, value, authenticated=False, ediv=None, rand=None): - self.value = value + self.value = value self.authenticated = authenticated - self.ediv = ediv - self.rand = rand + self.ediv = ediv + self.rand = rand @classmethod def from_dict(cls, key_dict): @@ -64,31 +70,33 @@ class PairingKeys: return key_dict def __init__(self): - self.address_type = None - self.ltk = None - self.ltk_central = None + self.address_type = None + self.ltk = None + self.ltk_central = None self.ltk_peripheral = None - self.irk = None - self.csrk = None - self.link_key = None # Classic + self.irk = None + self.csrk = None + self.link_key = None # Classic @staticmethod def key_from_dict(keys_dict, key_name): key_dict = keys_dict.get(key_name) - if key_dict is not None: - return PairingKeys.Key.from_dict(key_dict) + if key_dict is None: + return None + + return PairingKeys.Key.from_dict(key_dict) @staticmethod def from_dict(keys_dict): keys = PairingKeys() - keys.address_type = keys_dict.get('address_type') - keys.ltk = PairingKeys.key_from_dict(keys_dict, 'ltk') - keys.ltk_central = PairingKeys.key_from_dict(keys_dict, 'ltk_central') + keys.address_type = keys_dict.get('address_type') + keys.ltk = PairingKeys.key_from_dict(keys_dict, 'ltk') + keys.ltk_central = PairingKeys.key_from_dict(keys_dict, 'ltk_central') keys.ltk_peripheral = PairingKeys.key_from_dict(keys_dict, 'ltk_peripheral') - keys.irk = PairingKeys.key_from_dict(keys_dict, 'irk') - keys.csrk = PairingKeys.key_from_dict(keys_dict, 'csrk') - keys.link_key = PairingKeys.key_from_dict(keys_dict, 'link_key') + keys.irk = PairingKeys.key_from_dict(keys_dict, 'irk') + keys.csrk = PairingKeys.key_from_dict(keys_dict, 'csrk') + keys.link_key = PairingKeys.key_from_dict(keys_dict, 'link_key') return keys @@ -120,29 +128,33 @@ class PairingKeys: def print(self, prefix=''): keys_dict = self.to_dict() - for (property, value) in keys_dict.items(): - if type(value) is dict: - print(f'{prefix}{color(property, "cyan")}:') + for (container_property, value) in keys_dict.items(): + if isinstance(value, dict): + print(f'{prefix}{color(container_property, "cyan")}:') for (key_property, key_value) in value.items(): print(f'{prefix} {color(key_property, "green")}: {key_value}') else: - print(f'{prefix}{color(property, "cyan")}: {value}') + print(f'{prefix}{color(container_property, "cyan")}: {value}') # ----------------------------------------------------------------------------- class KeyStore: - async def delete(self, name): + async def delete(self, name: str): pass - async def update(self, name, keys): + async def update(self, name: str, keys: PairingKeys) -> None: pass - async def get(self, name): - return PairingKeys() + async def get(self, _name: str) -> Optional[PairingKeys]: + return None - async def get_all(self): + async def get_all(self) -> List[Tuple[str, PairingKeys]]: return [] + async def delete_all(self) -> None: + all_keys = await self.get_all() + await asyncio.gather(*(self.delete(name) for (name, _) in all_keys)) + async def get_resolving_keys(self): all_keys = await self.get_all() resolving_keys = [] @@ -161,26 +173,26 @@ class KeyStore: separator = '' for (name, keys) in entries: print(separator + prefix + color(name, 'yellow')) - keys.print(prefix = prefix + ' ') + keys.print(prefix=prefix + ' ') separator = '\n' @staticmethod - def create_for_device(device_config): - if device_config.keystore is None: - return None + def create_for_device(device: Device) -> KeyStore: + if device.config.keystore is None: + return MemoryKeyStore() - keystore_type = device_config.keystore.split(':', 1)[0] + keystore_type = device.config.keystore.split(':', 1)[0] if keystore_type == 'JsonKeyStore': - return JsonKeyStore.from_device_config(device_config) + return JsonKeyStore.from_device(device) - return None + return MemoryKeyStore() # ----------------------------------------------------------------------------- class JsonKeyStore(KeyStore): - APP_NAME = 'Bumble' - APP_AUTHOR = 'Google' - KEYS_DIR = 'Pairing' + APP_NAME = 'Bumble' + APP_AUTHOR = 'Google' + KEYS_DIR = 'Pairing' DEFAULT_NAMESPACE = '__DEFAULT__' def __init__(self, namespace, filename=None): @@ -188,12 +200,17 @@ class JsonKeyStore(KeyStore): if filename is None: # Use a default for the current user + + # Import here because this may not exist on all platforms + # pylint: disable=import-outside-toplevel import appdirs + self.directory_name = os.path.join( - appdirs.user_data_dir(self.APP_NAME, self.APP_AUTHOR), - self.KEYS_DIR + appdirs.user_data_dir(self.APP_NAME, self.APP_AUTHOR), self.KEYS_DIR + ) + json_filename = ( + f'{self.namespace}.json'.lower().replace(':', '-').replace('/p', '-p') ) - json_filename = f'{self.namespace}.json'.lower().replace(':', '-') self.filename = os.path.join(self.directory_name, json_filename) else: self.filename = filename @@ -202,11 +219,21 @@ class JsonKeyStore(KeyStore): logger.debug(f'JSON keystore: {self.filename}') @staticmethod - def from_device_config(device_config): - params = device_config.keystore.split(':', 1)[1:] - namespace = str(device_config.address) + def from_device(device: Device) -> Optional[JsonKeyStore]: + if not device.config.keystore: + return None + + params = device.config.keystore.split(':', 1)[1:] + + # Use a namespace based on the device address + if device.public_address not in (Address.ANY, Address.ANY_RANDOM): + namespace = str(device.public_address) + elif device.random_address != Address.ANY_RANDOM: + namespace = str(device.random_address) + else: + namespace = JsonKeyStore.DEFAULT_NAMESPACE if params: - filename = params[1] + filename = params[0] else: filename = None @@ -214,7 +241,7 @@ class JsonKeyStore(KeyStore): async def load(self): try: - with open(self.filename, 'r') as json_file: + with open(self.filename, 'r', encoding='utf-8') as json_file: return json.load(json_file) except FileNotFoundError: return {} @@ -226,13 +253,13 @@ class JsonKeyStore(KeyStore): # Save to a temporary file temp_filename = self.filename + '.tmp' - with open(temp_filename, 'w') as output: + with open(temp_filename, 'w', encoding='utf-8') as output: json.dump(db, output, sort_keys=True, indent=4) # Atomically replace the previous file - os.rename(temp_filename, self.filename) + os.replace(temp_filename, self.filename) - async def delete(self, name): + async def delete(self, name: str) -> None: db = await self.load() namespace = db.get(self.namespace) @@ -246,7 +273,7 @@ class JsonKeyStore(KeyStore): db = await self.load() namespace = db.setdefault(self.namespace, {}) - namespace[name] = keys.to_dict() + namespace.setdefault(name, {}).update(keys.to_dict()) await self.save(db) @@ -257,9 +284,18 @@ class JsonKeyStore(KeyStore): if namespace is None: return [] - return [(name, PairingKeys.from_dict(keys)) for (name, keys) in namespace.items()] + return [ + (name, PairingKeys.from_dict(keys)) for (name, keys) in namespace.items() + ] + + async def delete_all(self): + db = await self.load() + + db.pop(self.namespace, None) + + await self.save(db) - async def get(self, name): + async def get(self, name: str) -> Optional[PairingKeys]: db = await self.load() namespace = db.get(self.namespace) @@ -271,3 +307,24 @@ class JsonKeyStore(KeyStore): return None return PairingKeys.from_dict(keys) + + +# ----------------------------------------------------------------------------- +class MemoryKeyStore(KeyStore): + all_keys: Dict[str, PairingKeys] + + def __init__(self) -> None: + self.all_keys = {} + + async def delete(self, name: str) -> None: + if name in self.all_keys: + del self.all_keys[name] + + async def update(self, name: str, keys: PairingKeys) -> None: + self.all_keys[name] = keys + + async def get(self, name: str) -> Optional[PairingKeys]: + return self.all_keys.get(name) + + async def get_all(self) -> List[Tuple[str, PairingKeys]]: + return list(self.all_keys.items()) diff --git a/bumble/l2cap.py b/bumble/l2cap.py index 7a2ca2b..ef7fdab 100644 --- a/bumble/l2cap.py +++ b/bumble/l2cap.py @@ -15,16 +15,23 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import asyncio import logging import struct -from colors import color +from collections import deque +from pyee import EventEmitter +from typing import Dict, Type +from .colors import color from .core import BT_CENTRAL_ROLE, InvalidStateError, ProtocolError -from .hci import (HCI_LE_Connection_Update_Command, HCI_Object, key_with_value, - name_or_number) -from .utils import EventEmitter +from .hci import ( + HCI_LE_Connection_Update_Command, + HCI_Object, + key_with_value, + name_or_number, +) # ----------------------------------------------------------------------------- # Logging @@ -35,6 +42,9 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off +# pylint: disable=line-too-long + L2CAP_SIGNALING_CID = 0x01 L2CAP_LE_SIGNALING_CID = 0x05 @@ -43,13 +53,23 @@ L2CAP_MIN_BR_EDR_MTU = 48 L2CAP_DEFAULT_MTU = 2048 # Default value for the MTU we are willing to accept +L2CAP_DEFAULT_CONNECTIONLESS_MTU = 1024 + # See Bluetooth spec @ Vol 3, Part A - Table 2.1: CID name space on ACL-U, ASB-U, and AMP-U logical links L2CAP_ACL_U_DYNAMIC_CID_RANGE_START = 0x0040 L2CAP_ACL_U_DYNAMIC_CID_RANGE_END = 0xFFFF # See Bluetooth spec @ Vol 3, Part A - Table 2.2: CID name space on LE-U logical link L2CAP_LE_U_DYNAMIC_CID_RANGE_START = 0x0040 -L2CAP_LE_U_DYNAMIC_CID_RANGE_START = 0x007F +L2CAP_LE_U_DYNAMIC_CID_RANGE_END = 0x007F + +# PSM Range - See Bluetooth spec @ Vol 3, Part A / Table 4.5: PSM ranges and usage +L2CAP_PSM_DYNAMIC_RANGE_START = 0x1001 +L2CAP_PSM_DYNAMIC_RANGE_END = 0xFFFF + +# LE PSM Ranges - See Bluetooth spec @ Vol 3, Part A / Table 4.19: LE Credit Based Connection Request LE_PSM ranges +L2CAP_LE_PSM_DYNAMIC_RANGE_START = 0x0080 +L2CAP_LE_PSM_DYNAMIC_RANGE_END = 0x00FF # Frame types L2CAP_COMMAND_REJECT = 0x01 @@ -107,17 +127,28 @@ L2CAP_COMMAND_NOT_UNDERSTOOD_REASON = 0x0000 L2CAP_SIGNALING_MTU_EXCEEDED_REASON = 0x0001 L2CAP_INVALID_CID_IN_REQUEST_REASON = 0x0002 -L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU = 2048 -L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS = 2048 +L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_CREDITS = 65535 +L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MTU = 23 +L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MPS = 23 +L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_MPS = 65533 +L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU = 2046 +L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS = 2048 +L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS = 256 L2CAP_MAXIMUM_TRANSMISSION_UNIT_CONFIGURATION_OPTION_TYPE = 0x01 L2CAP_MTU_CONFIGURATION_PARAMETER_TYPE = 0x01 +# fmt: on +# pylint: enable=line-too-long + # ----------------------------------------------------------------------------- # Classes # ----------------------------------------------------------------------------- +# pylint: disable=invalid-name + + class L2CAP_PDU: ''' See Bluetooth spec @ Vol 3, Part A - 3 DATA PACKET FORMAT @@ -139,7 +170,7 @@ class L2CAP_PDU: return header + self.payload def __init__(self, cid, payload): - self.cid = cid + self.cid = cid self.payload = payload def __bytes__(self): @@ -154,8 +185,10 @@ class L2CAP_Control_Frame: ''' See Bluetooth spec @ Vol 3, Part A - 4 SIGNALING PACKET FORMATS ''' - classes = {} + + classes: Dict[int, Type[L2CAP_Control_Frame]] = {} code = 0 + name = None @staticmethod def from_bytes(pdu): @@ -172,7 +205,12 @@ class L2CAP_Control_Frame: self.identifier = pdu[1] length = struct.unpack_from('<H', pdu, 2)[0] if length + 4 != len(pdu): - logger.warn(color(f'!!! length mismatch: expected {len(pdu) - 4} but got {length}', 'red')) + logger.warning( + color( + f'!!! length mismatch: expected {len(pdu) - 4} but got {length}', + 'red', + ) + ) if hasattr(self, 'fields'): self.init_from_bytes(pdu, 4) return self @@ -185,17 +223,19 @@ class L2CAP_Control_Frame: def decode_configuration_options(data): options = [] while len(data) >= 2: - type = data[0] + value_type = data[0] length = data[1] - value = data[2:2 + length] - data = data[2 + length:] - options.append((type, value)) + value = data[2 : 2 + length] + data = data[2 + length :] + options.append((value_type, value)) return options @staticmethod def encode_configuration_options(options): - return b''.join([bytes([option[0], len(option[1])]) + option[1] for option in options]) + return b''.join( + [bytes([option[0], len(option[1])]) + option[1] for option in options] + ) @staticmethod def subclass(fields): @@ -203,7 +243,10 @@ class L2CAP_Control_Frame: cls.name = cls.__name__.upper() cls.code = key_with_value(L2CAP_CONTROL_FRAME_NAMES, cls.name) if cls.code is None: - raise KeyError(f'Control Frame name {cls.name} not found in L2CAP_CONTROL_FRAME_NAMES') + raise KeyError( + f'Control Frame name {cls.name} ' + 'not found in L2CAP_CONTROL_FRAME_NAMES' + ) cls.fields = fields # Register a factory for this class @@ -219,7 +262,11 @@ class L2CAP_Control_Frame: HCI_Object.init_from_fields(self, self.fields, kwargs) if pdu is None: data = HCI_Object.dict_to_bytes(kwargs, self.fields) - pdu = bytes([self.code, self.identifier]) + struct.pack('<H', len(data)) + data + pdu = ( + bytes([self.code, self.identifier]) + + struct.pack('<H', len(data)) + + data + ) self.pdu = pdu def init_from_bytes(self, pdu, offset): @@ -242,10 +289,16 @@ class L2CAP_Control_Frame: # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('reason', {'size': 2, 'mapper': lambda x: L2CAP_Command_Reject.reason_name(x)}), - ('data', '*') -]) +@L2CAP_Control_Frame.subclass( + # pylint: disable=unnecessary-lambda + [ + ( + 'reason', + {'size': 2, 'mapper': lambda x: L2CAP_Command_Reject.reason_name(x)}, + ), + ('data', '*'), + ] +) class L2CAP_Command_Reject(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.1 COMMAND REJECT @@ -258,7 +311,7 @@ class L2CAP_Command_Reject(L2CAP_Control_Frame): REASON_NAMES = { COMMAND_NOT_UNDERSTOOD: 'COMMAND_NOT_UNDERSTOOD', SIGNALING_MTU_EXCEEDED: 'SIGNALING_MTU_EXCEEDED', - INVALID_CID_IN_REQUEST: 'INVALID_CID_IN_REQUEST' + INVALID_CID_IN_REQUEST: 'INVALID_CID_IN_REQUEST', } @staticmethod @@ -267,59 +320,97 @@ class L2CAP_Command_Reject(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('psm', 2), - ('source_cid', 2) -]) +@L2CAP_Control_Frame.subclass( + # pylint: disable=unnecessary-lambda + [ + ( + 'psm', + { + 'parser': lambda data, offset: L2CAP_Connection_Request.parse_psm( + data, offset + ), + 'serializer': lambda value: L2CAP_Connection_Request.serialize_psm( + value + ), + }, + ), + ('source_cid', 2), + ] +) class L2CAP_Connection_Request(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.2 CONNECTION REQUEST ''' + @staticmethod + def parse_psm(data, offset=0): + psm_length = 2 + psm = data[offset] | data[offset + 1] << 8 + + # The PSM field extends until the first even octet (inclusive) + while data[offset + psm_length - 1] % 2 == 1: + psm |= data[offset + psm_length] << (8 * psm_length) + psm_length += 1 + + return offset + psm_length, psm + + @staticmethod + def serialize_psm(psm): + serialized = struct.pack('<H', psm & 0xFFFF) + psm >>= 16 + while psm: + serialized += bytes([psm & 0xFF]) + psm >>= 8 + + return serialized + # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('destination_cid', 2), - ('source_cid', 2), - ('result', {'size': 2, 'mapper': lambda x: L2CAP_Connection_Response.result_name(x)}), - ('status', 2) -]) +@L2CAP_Control_Frame.subclass( + # pylint: disable=unnecessary-lambda + [ + ('destination_cid', 2), + ('source_cid', 2), + ( + 'result', + {'size': 2, 'mapper': lambda x: L2CAP_Connection_Response.result_name(x)}, + ), + ('status', 2), + ] +) class L2CAP_Connection_Response(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.3 CONNECTION RESPONSE ''' - CONNECTION_SUCCESSFUL = 0x0000 - CONNECTION_PENDING = 0x0001 - CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED = 0x0002 - CONNECTION_REFUSED_SECURITY_BLOCK = 0x0003 - CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE = 0x0004 - CONNECTION_REFUSED_INVALID_SOURCE_CID = 0x0006 - CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED = 0x0007 - CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS = 0x000B - - CONNECTION_RESULT_NAMES = { - CONNECTION_SUCCESSFUL: 'CONNECTION_SUCCESSFUL', - CONNECTION_PENDING: 'CONNECTION_PENDING', - CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED: 'CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED', - CONNECTION_REFUSED_SECURITY_BLOCK: 'CONNECTION_REFUSED_SECURITY_BLOCK', - CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE: 'CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE', - CONNECTION_REFUSED_INVALID_SOURCE_CID: 'CONNECTION_REFUSED_INVALID_SOURCE_CID', + CONNECTION_SUCCESSFUL = 0x0000 + CONNECTION_PENDING = 0x0001 + CONNECTION_REFUSED_PSM_NOT_SUPPORTED = 0x0002 + CONNECTION_REFUSED_SECURITY_BLOCK = 0x0003 + CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE = 0x0004 + CONNECTION_REFUSED_INVALID_SOURCE_CID = 0x0006 + CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED = 0x0007 + CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS = 0x000B + + # pylint: disable=line-too-long + RESULT_NAMES = { + CONNECTION_SUCCESSFUL: 'CONNECTION_SUCCESSFUL', + CONNECTION_PENDING: 'CONNECTION_PENDING', + CONNECTION_REFUSED_PSM_NOT_SUPPORTED: 'CONNECTION_REFUSED_PSM_NOT_SUPPORTED', + CONNECTION_REFUSED_SECURITY_BLOCK: 'CONNECTION_REFUSED_SECURITY_BLOCK', + CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE: 'CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE', + CONNECTION_REFUSED_INVALID_SOURCE_CID: 'CONNECTION_REFUSED_INVALID_SOURCE_CID', CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED: 'CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED', - CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS: 'CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS' + CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS: 'CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS', } @staticmethod def result_name(result): - return name_or_number(L2CAP_Connection_Response.CONNECTION_RESULT_NAMES, result) + return name_or_number(L2CAP_Connection_Response.RESULT_NAMES, result) # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('destination_cid', 2), - ('flags', 2), - ('options', '*') -]) +@L2CAP_Control_Frame.subclass([('destination_cid', 2), ('flags', 2), ('options', '*')]) class L2CAP_Configure_Request(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.4 CONFIGURATION REQUEST @@ -327,31 +418,37 @@ class L2CAP_Configure_Request(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('source_cid', 2), - ('flags', 2), - ('result', {'size': 2, 'mapper': lambda x: L2CAP_Configure_Response.result_name(x)}), - ('options', '*') -]) +@L2CAP_Control_Frame.subclass( + # pylint: disable=unnecessary-lambda + [ + ('source_cid', 2), + ('flags', 2), + ( + 'result', + {'size': 2, 'mapper': lambda x: L2CAP_Configure_Response.result_name(x)}, + ), + ('options', '*'), + ] +) class L2CAP_Configure_Response(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.5 CONFIGURATION RESPONSE ''' - SUCCESS = 0x0000 + SUCCESS = 0x0000 FAILURE_UNACCEPTABLE_PARAMETERS = 0x0001 - FAILURE_REJECTED = 0x0002 - FAILURE_UNKNOWN_OPTIONS = 0x0003 - PENDING = 0x0004 - FAILURE_FLOW_SPEC_REJECTED = 0x0005 + FAILURE_REJECTED = 0x0002 + FAILURE_UNKNOWN_OPTIONS = 0x0003 + PENDING = 0x0004 + FAILURE_FLOW_SPEC_REJECTED = 0x0005 RESULT_NAMES = { - SUCCESS: 'SUCCESS', + SUCCESS: 'SUCCESS', FAILURE_UNACCEPTABLE_PARAMETERS: 'FAILURE_UNACCEPTABLE_PARAMETERS', - FAILURE_REJECTED: 'FAILURE_REJECTED', - FAILURE_UNKNOWN_OPTIONS: 'FAILURE_UNKNOWN_OPTIONS', - PENDING: 'PENDING', - FAILURE_FLOW_SPEC_REJECTED: 'FAILURE_FLOW_SPEC_REJECTED' + FAILURE_REJECTED: 'FAILURE_REJECTED', + FAILURE_UNKNOWN_OPTIONS: 'FAILURE_UNKNOWN_OPTIONS', + PENDING: 'PENDING', + FAILURE_FLOW_SPEC_REJECTED: 'FAILURE_FLOW_SPEC_REJECTED', } @staticmethod @@ -360,10 +457,7 @@ class L2CAP_Configure_Response(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('destination_cid', 2), - ('source_cid', 2) -]) +@L2CAP_Control_Frame.subclass([('destination_cid', 2), ('source_cid', 2)]) class L2CAP_Disconnection_Request(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.6 DISCONNECTION REQUEST @@ -371,10 +465,7 @@ class L2CAP_Disconnection_Request(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('destination_cid', 2), - ('source_cid', 2) -]) +@L2CAP_Control_Frame.subclass([('destination_cid', 2), ('source_cid', 2)]) class L2CAP_Disconnection_Response(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.7 DISCONNECTION RESPONSE @@ -382,9 +473,7 @@ class L2CAP_Disconnection_Response(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('data', '*') -]) +@L2CAP_Control_Frame.subclass([('data', '*')]) class L2CAP_Echo_Request(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.8 ECHO REQUEST @@ -392,9 +481,7 @@ class L2CAP_Echo_Request(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('data', '*') -]) +@L2CAP_Control_Frame.subclass([('data', '*')]) class L2CAP_Echo_Response(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.9 ECHO RESPONSE @@ -402,22 +489,43 @@ class L2CAP_Echo_Response(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('info_type', {'size': 2, 'mapper': lambda x: L2CAP_Information_Request.info_type_name(x)}) -]) +@L2CAP_Control_Frame.subclass( + [ + ( + 'info_type', + { + 'size': 2, + # pylint: disable-next=unnecessary-lambda + 'mapper': lambda x: L2CAP_Information_Request.info_type_name(x), + }, + ) + ] +) class L2CAP_Information_Request(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.10 INFORMATION REQUEST ''' - CONNECTIONLESS_MTU = 0x0001 + CONNECTIONLESS_MTU = 0x0001 EXTENDED_FEATURES_SUPPORTED = 0x0002 - FIXED_CHANNELS_SUPPORTED = 0x0003 + FIXED_CHANNELS_SUPPORTED = 0x0003 + + EXTENDED_FEATURE_FLOW_MODE_CONTROL = 0x0001 + EXTENDED_FEATURE_RETRANSMISSION_MODE = 0x0002 + EXTENDED_FEATURE_BIDIRECTIONAL_QOS = 0x0004 + EXTENDED_FEATURE_ENHANCED_RETRANSMISSION_MODE = 0x0008 + EXTENDED_FEATURE_STREAMING_MODE = 0x0010 + EXTENDED_FEATURE_FCS_OPTION = 0x0020 + EXTENDED_FEATURE_EXTENDED_FLOW_SPEC = 0x0040 + EXTENDED_FEATURE_FIXED_CHANNELS = 0x0080 + EXTENDED_FEATURE_EXTENDED_WINDOW_SIZE = 0x0100 + EXTENDED_FEATURE_UNICAST_CONNECTIONLESS_DATA = 0x0200 + EXTENDED_FEATURE_ENHANCED_CREDIT_BASE_FLOW_CONTROL = 0x0400 INFO_TYPE_NAMES = { - CONNECTIONLESS_MTU: 'CONNECTIONLESS_MTU', + CONNECTIONLESS_MTU: 'CONNECTIONLESS_MTU', EXTENDED_FEATURES_SUPPORTED: 'EXTENDED_FEATURES_SUPPORTED', - FIXED_CHANNELS_SUPPORTED: 'FIXED_CHANNELS_SUPPORTED' + FIXED_CHANNELS_SUPPORTED: 'FIXED_CHANNELS_SUPPORTED', } @staticmethod @@ -426,22 +534,26 @@ class L2CAP_Information_Request(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('info_type', {'size': 2, 'mapper': L2CAP_Information_Request.info_type_name}), - ('result', {'size': 2, 'mapper': lambda x: L2CAP_Information_Response.result_name(x)}), - ('data', '*') -]) +@L2CAP_Control_Frame.subclass( + [ + ('info_type', {'size': 2, 'mapper': L2CAP_Information_Request.info_type_name}), + ( + 'result', + # pylint: disable-next=unnecessary-lambda + {'size': 2, 'mapper': lambda x: L2CAP_Information_Response.result_name(x)}, + ), + ('data', '*'), + ] +) class L2CAP_Information_Response(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.11 INFORMATION RESPONSE ''' - SUCCESS = 0x00 + + SUCCESS = 0x00 NOT_SUPPORTED = 0x01 - RESULT_NAMES = { - SUCCESS: 'SUCCESS', - NOT_SUPPORTED: 'NOT_SUPPORTED' - } + RESULT_NAMES = {SUCCESS: 'SUCCESS', NOT_SUPPORTED: 'NOT_SUPPORTED'} @staticmethod def result_name(result): @@ -449,12 +561,9 @@ class L2CAP_Information_Response(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('interval_min', 2), - ('interval_max', 2), - ('slave_latency', 2), - ('timeout_multiplier', 2) -]) +@L2CAP_Control_Frame.subclass( + [('interval_min', 2), ('interval_max', 2), ('latency', 2), ('timeout', 2)] +) class L2CAP_Connection_Parameter_Update_Request(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.20 CONNECTION PARAMETER UPDATE REQUEST @@ -462,9 +571,7 @@ class L2CAP_Connection_Parameter_Update_Request(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('result', 2) -]) +@L2CAP_Control_Frame.subclass([('result', 2)]) class L2CAP_Connection_Parameter_Update_Response(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.21 CONNECTION PARAMETER UPDATE RESPONSE @@ -472,66 +579,75 @@ class L2CAP_Connection_Parameter_Update_Response(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('le_psm', 2), - ('source_cid', 2), - ('mtu', 2), - ('mps', 2), - ('initial_credits', 2) -]) +@L2CAP_Control_Frame.subclass( + [('le_psm', 2), ('source_cid', 2), ('mtu', 2), ('mps', 2), ('initial_credits', 2)] +) class L2CAP_LE_Credit_Based_Connection_Request(L2CAP_Control_Frame): ''' - See Bluetooth spec @ Vol 3, Part A - 4.22 LE CREDIT BASED CONNECTION REQUEST (CODE 0x14) + See Bluetooth spec @ Vol 3, Part A - 4.22 LE CREDIT BASED CONNECTION REQUEST + (CODE 0x14) ''' # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('destination_cid', 2), - ('mtu', 2), - ('mps', 2), - ('initial_credits', 2), - ('result', {'size': 2, 'mapper': lambda x: L2CAP_LE_Credit_Based_Connection_Response.result_name(x)}) -]) +@L2CAP_Control_Frame.subclass( + # pylint: disable=unnecessary-lambda,line-too-long + [ + ('destination_cid', 2), + ('mtu', 2), + ('mps', 2), + ('initial_credits', 2), + ( + 'result', + { + 'size': 2, + 'mapper': lambda x: L2CAP_LE_Credit_Based_Connection_Response.result_name( + x + ), + }, + ), + ] +) class L2CAP_LE_Credit_Based_Connection_Response(L2CAP_Control_Frame): ''' - See Bluetooth spec @ Vol 3, Part A - 4.23 LE CREDIT BASED CONNECTION RESPONSE (CODE 0x15) + See Bluetooth spec @ Vol 3, Part A - 4.23 LE CREDIT BASED CONNECTION RESPONSE + (CODE 0x15) ''' - CONNECTION_SUCCESSFUL = 0x0000 - CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED = 0x0002 - CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE = 0x0004 - CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION = 0x0005 - CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION = 0x0006 + CONNECTION_SUCCESSFUL = 0x0000 + CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED = 0x0002 + CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE = 0x0004 + CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION = 0x0005 + CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION = 0x0006 CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0x0007 - CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION = 0x0008 - CONNECTION_REFUSED_INVALID_SOURCE_CID = 0x0009 - CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED = 0x000A - CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS = 0x000B - - CONNECTION_RESULT_NAMES = { - CONNECTION_SUCCESSFUL: 'CONNECTION_SUCCESSFUL', - CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED: 'CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED', - CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE: 'CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE', - CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION: 'CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION', - CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION: 'CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION', + CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION = 0x0008 + CONNECTION_REFUSED_INVALID_SOURCE_CID = 0x0009 + CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED = 0x000A + CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS = 0x000B + + # pylint: disable=line-too-long + RESULT_NAMES = { + CONNECTION_SUCCESSFUL: 'CONNECTION_SUCCESSFUL', + CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED: 'CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED', + CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE: 'CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE', + CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION: 'CONNECTION_REFUSED_INSUFFICIENT_AUTHENTICATION', + CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION: 'CONNECTION_REFUSED_INSUFFICIENT_AUTHORIZATION', CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION_KEY_SIZE: 'CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION_KEY_SIZE', - CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION: 'CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION', - CONNECTION_REFUSED_INVALID_SOURCE_CID: 'CONNECTION_REFUSED_INVALID_SOURCE_CID', - CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED: 'CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED', - CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS: 'CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS' + CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION: 'CONNECTION_REFUSED_INSUFFICIENT_ENCRYPTION', + CONNECTION_REFUSED_INVALID_SOURCE_CID: 'CONNECTION_REFUSED_INVALID_SOURCE_CID', + CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED: 'CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED', + CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS: 'CONNECTION_REFUSED_UNACCEPTABLE_PARAMETERS', } @staticmethod def result_name(result): - return name_or_number(L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_RESULT_NAMES, result) + return name_or_number( + L2CAP_LE_Credit_Based_Connection_Response.RESULT_NAMES, result + ) # ----------------------------------------------------------------------------- -@L2CAP_Control_Frame.subclass([ - ('cid', 2), - ('credits', 2) -]) +@L2CAP_Control_Frame.subclass([('cid', 2), ('credits', 2)]) class L2CAP_LE_Flow_Control_Credit(L2CAP_Control_Frame): ''' See Bluetooth spec @ Vol 3, Part A - 4.24 LE FLOW CONTROL CREDIT (CODE 0x16) @@ -541,72 +657,77 @@ class L2CAP_LE_Flow_Control_Credit(L2CAP_Control_Frame): # ----------------------------------------------------------------------------- class Channel(EventEmitter): # States - CLOSED = 0x00 - WAIT_CONNECT = 0x01 - WAIT_CONNECT_RSP = 0x02 - OPEN = 0x03 - WAIT_DISCONNECT = 0x04 - WAIT_CREATE = 0x05 - WAIT_CREATE_RSP = 0x06 - WAIT_MOVE = 0x07 - WAIT_MOVE_RSP = 0x08 + CLOSED = 0x00 + WAIT_CONNECT = 0x01 + WAIT_CONNECT_RSP = 0x02 + OPEN = 0x03 + WAIT_DISCONNECT = 0x04 + WAIT_CREATE = 0x05 + WAIT_CREATE_RSP = 0x06 + WAIT_MOVE = 0x07 + WAIT_MOVE_RSP = 0x08 WAIT_MOVE_CONFIRM = 0x09 - WAIT_CONFIRM_RSP = 0x0A + WAIT_CONFIRM_RSP = 0x0A # CONFIG substates - WAIT_CONFIG = 0x10 - WAIT_SEND_CONFIG = 0x11 + WAIT_CONFIG = 0x10 + WAIT_SEND_CONFIG = 0x11 WAIT_CONFIG_REQ_RSP = 0x12 - WAIT_CONFIG_RSP = 0x13 - WAIT_CONFIG_REQ = 0x14 - WAIT_IND_FINAL_RSP = 0x15 - WAIT_FINAL_RSP = 0x16 - WAIT_CONTROL_IND = 0x17 + WAIT_CONFIG_RSP = 0x13 + WAIT_CONFIG_REQ = 0x14 + WAIT_IND_FINAL_RSP = 0x15 + WAIT_FINAL_RSP = 0x16 + WAIT_CONTROL_IND = 0x17 STATE_NAMES = { - CLOSED: 'CLOSED', - WAIT_CONNECT: 'WAIT_CONNECT', - WAIT_CONNECT_RSP: 'WAIT_CONNECT_RSP', - OPEN: 'OPEN', - WAIT_DISCONNECT: 'WAIT_DISCONNECT', - WAIT_CREATE: 'WAIT_CREATE', - WAIT_CREATE_RSP: 'WAIT_CREATE_RSP', - WAIT_MOVE: 'WAIT_MOVE', - WAIT_MOVE_RSP: 'WAIT_MOVE_RSP', + CLOSED: 'CLOSED', + WAIT_CONNECT: 'WAIT_CONNECT', + WAIT_CONNECT_RSP: 'WAIT_CONNECT_RSP', + OPEN: 'OPEN', + WAIT_DISCONNECT: 'WAIT_DISCONNECT', + WAIT_CREATE: 'WAIT_CREATE', + WAIT_CREATE_RSP: 'WAIT_CREATE_RSP', + WAIT_MOVE: 'WAIT_MOVE', + WAIT_MOVE_RSP: 'WAIT_MOVE_RSP', WAIT_MOVE_CONFIRM: 'WAIT_MOVE_CONFIRM', - WAIT_CONFIRM_RSP: 'WAIT_CONFIRM_RSP', - - WAIT_CONFIG: 'WAIT_CONFIG', - WAIT_SEND_CONFIG: 'WAIT_SEND_CONFIG', + WAIT_CONFIRM_RSP: 'WAIT_CONFIRM_RSP', + WAIT_CONFIG: 'WAIT_CONFIG', + WAIT_SEND_CONFIG: 'WAIT_SEND_CONFIG', WAIT_CONFIG_REQ_RSP: 'WAIT_CONFIG_REQ_RSP', - WAIT_CONFIG_RSP: 'WAIT_CONFIG_RSP', - WAIT_CONFIG_REQ: 'WAIT_CONFIG_REQ', - WAIT_IND_FINAL_RSP: 'WAIT_IND_FINAL_RSP', - WAIT_FINAL_RSP: 'WAIT_FINAL_RSP', - WAIT_CONTROL_IND: 'WAIT_CONTROL_IND' + WAIT_CONFIG_RSP: 'WAIT_CONFIG_RSP', + WAIT_CONFIG_REQ: 'WAIT_CONFIG_REQ', + WAIT_IND_FINAL_RSP: 'WAIT_IND_FINAL_RSP', + WAIT_FINAL_RSP: 'WAIT_FINAL_RSP', + WAIT_CONTROL_IND: 'WAIT_CONTROL_IND', } def __init__(self, manager, connection, signaling_cid, psm, source_cid, mtu): super().__init__() - self.manager = manager - self.connection = connection - self.signaling_cid = signaling_cid - self.state = Channel.CLOSED - self.mtu = mtu - self.psm = psm - self.source_cid = source_cid - self.destination_cid = 0 - self.response = None + self.manager = manager + self.connection = connection + self.signaling_cid = signaling_cid + self.state = Channel.CLOSED + self.mtu = mtu + self.psm = psm + self.source_cid = source_cid + self.destination_cid = 0 + self.response = None self.connection_result = None - self.sink = None + self.disconnection_result = None + self.sink = None def change_state(self, new_state): - logger.debug(f'{self} state change -> {color(Channel.STATE_NAMES[new_state], "cyan")}') + logger.debug( + f'{self} state change -> {color(Channel.STATE_NAMES[new_state], "cyan")}' + ) self.state = new_state def send_pdu(self, pdu): self.manager.send_pdu(self.connection, self.destination_cid, pdu) + def send_control_frame(self, frame): + self.manager.send_control_frame(self.connection, self.signaling_cid, frame) + async def send_request(self, request): # Check that there isn't already a request pending if self.response: @@ -623,29 +744,39 @@ class Channel(EventEmitter): self.response.set_result(pdu) self.response = None elif self.sink: + # pylint: disable=not-callable self.sink(pdu) else: - logger.warn(color('received pdu without a pending request or sink', 'red')) - - def send_control_frame(self, frame): - self.manager.send_control_frame(self.connection, self.signaling_cid, frame) + logger.warning( + color('received pdu without a pending request or sink', 'red') + ) async def connect(self): if self.state != Channel.CLOSED: raise InvalidStateError('invalid state') + # Check that we can start a new connection + if self.connection_result: + raise RuntimeError('connection already pending') + self.change_state(Channel.WAIT_CONNECT_RSP) self.send_control_frame( L2CAP_Connection_Request( - identifier = self.manager.next_identifier(self.connection), - psm = self.psm, - source_cid = self.source_cid + identifier=self.manager.next_identifier(self.connection), + psm=self.psm, + source_cid=self.source_cid, ) ) - # Create a future to wait for the state machine to get to a success or error state + # Create a future to wait for the state machine to get to a success or error + # state self.connection_result = asyncio.get_running_loop().create_future() - return await self.connection_result + + # Wait for the connection to succeed or fail + try: + return await self.connection_result + finally: + self.connection_result = None async def disconnect(self): if self.state != Channel.OPEN: @@ -654,27 +785,37 @@ class Channel(EventEmitter): self.change_state(Channel.WAIT_DISCONNECT) self.send_control_frame( L2CAP_Disconnection_Request( - identifier = self.manager.next_identifier(self.connection), - destination_cid = self.destination_cid, - source_cid = self.source_cid + identifier=self.manager.next_identifier(self.connection), + destination_cid=self.destination_cid, + source_cid=self.source_cid, ) ) - # Create a future to wait for the state machine to get to a success or error state + # Create a future to wait for the state machine to get to a success or error + # state self.disconnection_result = asyncio.get_running_loop().create_future() return await self.disconnection_result + def abort(self): + if self.state == self.OPEN: + self.change_state(self.CLOSED) + self.emit('close') + def send_configure_request(self): - options = L2CAP_Control_Frame.encode_configuration_options([( - L2CAP_MAXIMUM_TRANSMISSION_UNIT_CONFIGURATION_OPTION_TYPE, - struct.pack('<H', L2CAP_DEFAULT_MTU) - )]) + options = L2CAP_Control_Frame.encode_configuration_options( + [ + ( + L2CAP_MAXIMUM_TRANSMISSION_UNIT_CONFIGURATION_OPTION_TYPE, + struct.pack('<H', L2CAP_DEFAULT_MTU), + ) + ] + ) self.send_control_frame( L2CAP_Configure_Request( - identifier = self.manager.next_identifier(self.connection), - destination_cid = self.destination_cid, - flags = 0x0000, - options = options + identifier=self.manager.next_identifier(self.connection), + destination_cid=self.destination_cid, + flags=0x0000, + options=options, ) ) @@ -683,11 +824,11 @@ class Channel(EventEmitter): self.change_state(Channel.WAIT_CONNECT) self.send_control_frame( L2CAP_Connection_Response( - identifier = request.identifier, - destination_cid = self.source_cid, - source_cid = self.destination_cid, - result = L2CAP_Connection_Response.CONNECTION_SUCCESSFUL, - status = 0x0000 + identifier=request.identifier, + destination_cid=self.source_cid, + source_cid=self.destination_cid, + result=L2CAP_Connection_Response.CONNECTION_SUCCESSFUL, + status=0x0000, ) ) self.change_state(Channel.WAIT_CONFIG) @@ -696,7 +837,7 @@ class Channel(EventEmitter): def on_connection_response(self, response): if self.state != Channel.WAIT_CONNECT_RSP: - logger.warn(color('invalid state', 'red')) + logger.warning(color('invalid state', 'red')) return if response.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL: @@ -712,17 +853,18 @@ class Channel(EventEmitter): ProtocolError( response.result, 'l2cap', - L2CAP_Connection_Response.result_name(response.result)) + L2CAP_Connection_Response.result_name(response.result), + ) ) self.connection_result = None def on_configure_request(self, request): - if ( - self.state != Channel.WAIT_CONFIG and - self.state != Channel.WAIT_CONFIG_REQ and - self.state != Channel.WAIT_CONFIG_REQ_RSP + if self.state not in ( + Channel.WAIT_CONFIG, + Channel.WAIT_CONFIG_REQ, + Channel.WAIT_CONFIG_REQ_RSP, ): - logger.warn(color('invalid state', 'red')) + logger.warning(color('invalid state', 'red')) return # Decode the options @@ -734,11 +876,11 @@ class Channel(EventEmitter): self.send_control_frame( L2CAP_Configure_Response( - identifier = request.identifier, - source_cid = self.destination_cid, - flags = 0x0000, - result = L2CAP_Configure_Response.SUCCESS, - options = request.options # TODO: don't accept everthing blindly + identifier=request.identifier, + source_cid=self.destination_cid, + flags=0x0000, + result=L2CAP_Configure_Response.SUCCESS, + options=request.options, # TODO: don't accept everything blindly ) ) if self.state == Channel.WAIT_CONFIG: @@ -758,50 +900,61 @@ class Channel(EventEmitter): if response.result == L2CAP_Configure_Response.SUCCESS: if self.state == Channel.WAIT_CONFIG_REQ_RSP: self.change_state(Channel.WAIT_CONFIG_REQ) - elif self.state == Channel.WAIT_CONFIG_RSP or self.state == Channel.WAIT_CONTROL_IND: + elif self.state in (Channel.WAIT_CONFIG_RSP, Channel.WAIT_CONTROL_IND): self.change_state(Channel.OPEN) if self.connection_result: self.connection_result.set_result(None) self.connection_result = None self.emit('open') else: - logger.warn(color('invalid state', 'red')) - elif response.result == L2CAP_Configure_Response.FAILURE_UNACCEPTABLE_PARAMETERS: + logger.warning(color('invalid state', 'red')) + elif ( + response.result == L2CAP_Configure_Response.FAILURE_UNACCEPTABLE_PARAMETERS + ): # Re-configure with what's suggested in the response self.send_control_frame( L2CAP_Configure_Request( - identifier = self.manager.next_identifier(self.connection), - destination_cid = self.destination_cid, - flags = 0x0000, - options = response.options + identifier=self.manager.next_identifier(self.connection), + destination_cid=self.destination_cid, + flags=0x0000, + options=response.options, ) ) else: - logger.warn(color(f'!!! configuration rejected: {L2CAP_Configure_Response.result_name(response.result)}', 'red')) + logger.warning( + color( + '!!! configuration rejected: ' + f'{L2CAP_Configure_Response.result_name(response.result)}', + 'red', + ) + ) # TODO: decide how to fail gracefully def on_disconnection_request(self, request): - if self.state == Channel.OPEN or self.state == Channel.WAIT_DISCONNECT: + if self.state in (Channel.OPEN, Channel.WAIT_DISCONNECT): self.send_control_frame( L2CAP_Disconnection_Response( - identifier = request.identifier, - destination_cid = request.destination_cid, - source_cid = request.source_cid + identifier=request.identifier, + destination_cid=request.destination_cid, + source_cid=request.source_cid, ) ) self.change_state(Channel.CLOSED) self.emit('close') self.manager.on_channel_closed(self) else: - logger.warn(color('invalid state', 'red')) + logger.warning(color('invalid state', 'red')) def on_disconnection_response(self, response): if self.state != Channel.WAIT_DISCONNECT: - logger.warn(color('invalid state', 'red')) + logger.warning(color('invalid state', 'red')) return - if response.destination_cid != self.destination_cid or response.source_cid != self.source_cid: - logger.warn('unexpected source or destination CID') + if ( + response.destination_cid != self.destination_cid + or response.source_cid != self.source_cid + ): + logger.warning('unexpected source or destination CID') return self.change_state(Channel.CLOSED) @@ -812,62 +965,594 @@ class Channel(EventEmitter): self.manager.on_channel_closed(self) def __str__(self): - return f'Channel({self.source_cid}->{self.destination_cid}, PSM={self.psm}, MTU={self.mtu}, state={Channel.STATE_NAMES[self.state]})' + return ( + f'Channel({self.source_cid}->{self.destination_cid}, ' + f'PSM={self.psm}, ' + f'MTU={self.mtu}, ' + f'state={Channel.STATE_NAMES[self.state]})' + ) + + +# ----------------------------------------------------------------------------- +class LeConnectionOrientedChannel(EventEmitter): + """ + LE Credit-based Connection Oriented Channel + """ + + INIT = 0 + CONNECTED = 1 + CONNECTING = 2 + DISCONNECTING = 3 + DISCONNECTED = 4 + CONNECTION_ERROR = 5 + + STATE_NAMES = { + INIT: 'INIT', + CONNECTED: 'CONNECTED', + CONNECTING: 'CONNECTING', + DISCONNECTING: 'DISCONNECTING', + DISCONNECTED: 'DISCONNECTED', + CONNECTION_ERROR: 'CONNECTION_ERROR', + } + + @staticmethod + def state_name(state): + return name_or_number(LeConnectionOrientedChannel.STATE_NAMES, state) + + def __init__( + self, + manager, + connection, + le_psm, + source_cid, + destination_cid, + mtu, + mps, + credits, # pylint: disable=redefined-builtin + peer_mtu, + peer_mps, + peer_credits, + connected, + ): + super().__init__() + self.manager = manager + self.connection = connection + self.le_psm = le_psm + self.source_cid = source_cid + self.destination_cid = destination_cid + self.mtu = mtu + self.mps = mps + self.credits = credits + self.peer_mtu = peer_mtu + self.peer_mps = peer_mps + self.peer_credits = peer_credits + self.peer_max_credits = self.peer_credits + self.peer_credits_threshold = self.peer_max_credits // 2 + self.in_sdu = None + self.in_sdu_length = 0 + self.out_queue = deque() + self.out_sdu = None + self.sink = None + self.connected = False + self.connection_result = None + self.disconnection_result = None + self.drained = asyncio.Event() + + self.drained.set() + + if connected: + self.state = LeConnectionOrientedChannel.CONNECTED + else: + self.state = LeConnectionOrientedChannel.INIT + + def change_state(self, new_state): + logger.debug( + f'{self} state change -> {color(self.state_name(new_state), "cyan")}' + ) + self.state = new_state + + if new_state == self.CONNECTED: + self.emit('open') + elif new_state == self.DISCONNECTED: + self.emit('close') + + def send_pdu(self, pdu): + self.manager.send_pdu(self.connection, self.destination_cid, pdu) + + def send_control_frame(self, frame): + self.manager.send_control_frame(self.connection, L2CAP_LE_SIGNALING_CID, frame) + + async def connect(self): + # Check that we're in the right state + if self.state != self.INIT: + raise InvalidStateError('not in a connectable state') + + # Check that we can start a new connection + identifier = self.manager.next_identifier(self.connection) + if identifier in self.manager.le_coc_requests: + raise RuntimeError('too many concurrent connection requests') + + self.change_state(self.CONNECTING) + request = L2CAP_LE_Credit_Based_Connection_Request( + identifier=identifier, + le_psm=self.le_psm, + source_cid=self.source_cid, + mtu=self.mtu, + mps=self.mps, + initial_credits=self.peer_credits, + ) + self.manager.le_coc_requests[identifier] = request + self.send_control_frame(request) + + # Create a future to wait for the response + self.connection_result = asyncio.get_running_loop().create_future() + + # Wait for the connection to succeed or fail + return await self.connection_result + + async def disconnect(self): + # Check that we're connected + if self.state != self.CONNECTED: + raise InvalidStateError('not connected') + + self.change_state(self.DISCONNECTING) + self.flush_output() + self.send_control_frame( + L2CAP_Disconnection_Request( + identifier=self.manager.next_identifier(self.connection), + destination_cid=self.destination_cid, + source_cid=self.source_cid, + ) + ) + + # Create a future to wait for the state machine to get to a success or error + # state + self.disconnection_result = asyncio.get_running_loop().create_future() + return await self.disconnection_result + + def abort(self): + if self.state == self.CONNECTED: + self.change_state(self.DISCONNECTED) + + def on_pdu(self, pdu): + if self.sink is None: + logger.warning('received pdu without a sink') + return + + if self.state != self.CONNECTED: + logger.warning('received PDU while not connected, dropping') + + # Manage the peer credits + if self.peer_credits == 0: + logger.warning('received LE frame when peer out of credits') + else: + self.peer_credits -= 1 + if self.peer_credits <= self.peer_credits_threshold: + # The credits fell below the threshold, replenish them to the max + self.send_control_frame( + L2CAP_LE_Flow_Control_Credit( + identifier=self.manager.next_identifier(self.connection), + cid=self.source_cid, + credits=self.peer_max_credits - self.peer_credits, + ) + ) + self.peer_credits = self.peer_max_credits + + # Check if this starts a new SDU + if self.in_sdu is None: + # Start a new SDU + self.in_sdu = pdu + else: + # Continue an SDU + self.in_sdu += pdu + + # Check if the SDU is complete + if self.in_sdu_length == 0: + # We don't know the size yet, check if we have received the header to + # compute it + if len(self.in_sdu) >= 2: + self.in_sdu_length = struct.unpack_from('<H', self.in_sdu, 0)[0] + if self.in_sdu_length == 0: + # We'll compute it later + return + if len(self.in_sdu) < 2 + self.in_sdu_length: + # Not complete yet + logger.debug( + f'SDU: {len(self.in_sdu) - 2} of {self.in_sdu_length} bytes received' + ) + return + if len(self.in_sdu) != 2 + self.in_sdu_length: + # Overflow + logger.warning( + f'SDU overflow: sdu_length={self.in_sdu_length}, ' + f'received {len(self.in_sdu) - 2}' + ) + # TODO: we should disconnect + self.in_sdu = None + self.in_sdu_length = 0 + return + + # Send the SDU to the sink + logger.debug(f'SDU complete: 2+{len(self.in_sdu) - 2} bytes') + self.sink(self.in_sdu[2:]) # pylint: disable=not-callable + + # Prepare for a new SDU + self.in_sdu = None + self.in_sdu_length = 0 + + def on_connection_response(self, response): + # Look for a matching pending response result + if self.connection_result is None: + logger.warning( + f'received unexpected connection response (id={response.identifier})' + ) + return + + if ( + response.result + == L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_SUCCESSFUL + ): + self.destination_cid = response.destination_cid + self.peer_mtu = response.mtu + self.peer_mps = response.mps + self.credits = response.initial_credits + self.connected = True + self.connection_result.set_result(self) + self.change_state(self.CONNECTED) + else: + self.connection_result.set_exception( + ProtocolError( + response.result, + 'l2cap', + L2CAP_LE_Credit_Based_Connection_Response.result_name( + response.result + ), + ) + ) + self.change_state(self.CONNECTION_ERROR) + + # Cleanup + self.connection_result = None + + def on_credits(self, credits): # pylint: disable=redefined-builtin + self.credits += credits + logger.debug(f'received {credits} credits, total = {self.credits}') + + # Try to send more data if we have any queued up + self.process_output() + + def on_disconnection_request(self, request): + self.send_control_frame( + L2CAP_Disconnection_Response( + identifier=request.identifier, + destination_cid=request.destination_cid, + source_cid=request.source_cid, + ) + ) + self.change_state(self.DISCONNECTED) + self.flush_output() + + def on_disconnection_response(self, response): + if self.state != self.DISCONNECTING: + logger.warning(color('invalid state', 'red')) + return + + if ( + response.destination_cid != self.destination_cid + or response.source_cid != self.source_cid + ): + logger.warning('unexpected source or destination CID') + return + + self.change_state(self.DISCONNECTED) + if self.disconnection_result: + self.disconnection_result.set_result(None) + self.disconnection_result = None + + def flush_output(self): + self.out_queue.clear() + self.out_sdu = None + + def process_output(self): + while self.credits > 0: + if self.out_sdu is not None: + # Finish the current SDU + packet = self.out_sdu[: self.peer_mps] + self.send_pdu(packet) + self.credits -= 1 + logger.debug(f'sent {len(packet)} bytes, {self.credits} credits left') + if len(packet) == len(self.out_sdu): + # We sent everything + self.out_sdu = None + else: + # Keep what's still left to send + self.out_sdu = self.out_sdu[len(packet) :] + continue + + if self.out_queue: + # Create the next SDU (2 bytes header plus up to MTU bytes payload) + logger.debug( + f'assembling SDU from {len(self.out_queue)} packets in output queue' + ) + payload = b'' + while self.out_queue and len(payload) < self.peer_mtu: + # We can add more data to the payload + chunk = self.out_queue[0][: self.peer_mtu - len(payload)] + payload += chunk + self.out_queue[0] = self.out_queue[0][len(chunk) :] + if len(self.out_queue[0]) == 0: + # We consumed the entire buffer, remove it + self.out_queue.popleft() + logger.debug( + f'packet completed, {len(self.out_queue)} left in queue' + ) + + # Construct the SDU with its header + assert len(payload) != 0 + logger.debug(f'SDU complete: {len(payload)} payload bytes') + self.out_sdu = struct.pack('<H', len(payload)) + payload + else: + # Nothing left to send for now + self.drained.set() + return + + def write(self, data): + if self.state != self.CONNECTED: + logger.warning('not connected, dropping data') + return + + # Queue the data + self.out_queue.append(data) + self.drained.clear() + logger.debug( + f'{len(data)} bytes packet queued, {len(self.out_queue)} packets in queue' + ) + + # Send what we can + self.process_output() + + async def drain(self): + await self.drained.wait() + + def pause_reading(self): + # TODO: not implemented yet + pass + + def resume_reading(self): + # TODO: not implemented yet + pass + + def __str__(self): + return ( + f'CoC({self.source_cid}->{self.destination_cid}, ' + f'State={self.state_name(self.state)}, ' + f'PSM={self.le_psm}, ' + f'MTU={self.mtu}/{self.peer_mtu}, ' + f'MPS={self.mps}/{self.peer_mps}, ' + f'credits={self.credits}/{self.peer_credits})' + ) # ----------------------------------------------------------------------------- class ChannelManager: - def __init__(self): - self.host = None - self.channels = {} # Channels, mapped by connection and cid + def __init__( + self, extended_features=(), connectionless_mtu=L2CAP_DEFAULT_CONNECTIONLESS_MTU + ): + self._host = None self.identifiers = {} # Incrementing identifier values by connection - self.servers = {} # Servers accepting connections, by PSM + self.channels = {} # All channels, mapped by connection and source cid + self.fixed_channels = { # Fixed channel handlers, mapped by cid + L2CAP_SIGNALING_CID: None, + L2CAP_LE_SIGNALING_CID: None, + } + self.servers = {} # Servers accepting connections, by PSM + self.le_coc_channels = ( + {} + ) # LE CoC channels, mapped by connection and destination cid + self.le_coc_servers = {} # LE CoC - Servers accepting connections, by PSM + self.le_coc_requests = {} # LE CoC connection requests, by identifier + self.extended_features = extended_features + self.connectionless_mtu = connectionless_mtu + + @property + def host(self): + return self._host + + @host.setter + def host(self, host): + if self._host is not None: + self._host.remove_listener('disconnection', self.on_disconnection) + self._host = host + if host is not None: + host.on('disconnection', self.on_disconnection) def find_channel(self, connection_handle, cid): if connection_channels := self.channels.get(connection_handle): return connection_channels.get(cid) + return None + + def find_le_coc_channel(self, connection_handle, cid): + if connection_channels := self.le_coc_channels.get(connection_handle): + return connection_channels.get(cid) + + return None + @staticmethod def find_free_br_edr_cid(channels): # Pick the smallest valid CID that's not already in the list # (not necessarily the most efficient algorithm, but the list of CID is # very small in practice) - for cid in range(L2CAP_ACL_U_DYNAMIC_CID_RANGE_START, L2CAP_ACL_U_DYNAMIC_CID_RANGE_END + 1): + for cid in range( + L2CAP_ACL_U_DYNAMIC_CID_RANGE_START, L2CAP_ACL_U_DYNAMIC_CID_RANGE_END + 1 + ): + if cid not in channels: + return cid + + raise RuntimeError('no free CID available') + + @staticmethod + def find_free_le_cid(channels): + # Pick the smallest valid CID that's not already in the list + # (not necessarily the most efficient algorithm, but the list of CID is + # very small in practice) + for cid in range( + L2CAP_LE_U_DYNAMIC_CID_RANGE_START, L2CAP_LE_U_DYNAMIC_CID_RANGE_END + 1 + ): if cid not in channels: return cid + raise RuntimeError('no free CID') + + @staticmethod + def check_le_coc_parameters(max_credits, mtu, mps): + if ( + max_credits < 1 + or max_credits > L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_CREDITS + ): + raise ValueError('max credits out of range') + if mtu < L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MTU: + raise ValueError('MTU too small') + if ( + mps < L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MPS + or mps > L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_MPS + ): + raise ValueError('MPS out of range') + def next_identifier(self, connection): identifier = (self.identifiers.setdefault(connection.handle, 0) + 1) % 256 self.identifiers[connection.handle] = identifier return identifier + def register_fixed_channel(self, cid, handler): + self.fixed_channels[cid] = handler + + def deregister_fixed_channel(self, cid): + if cid in self.fixed_channels: + del self.fixed_channels[cid] + def register_server(self, psm, server): + if psm == 0: + # Find a free PSM + for candidate in range( + L2CAP_PSM_DYNAMIC_RANGE_START, L2CAP_PSM_DYNAMIC_RANGE_END + 1, 2 + ): + if (candidate >> 8) % 2 == 1: + continue + if candidate in self.servers: + continue + psm = candidate + break + else: + raise InvalidStateError('no free PSM') + else: + # Check that the PSM isn't already in use + if psm in self.servers: + raise ValueError('PSM already in use') + + # Check that the PSM is valid + if psm % 2 == 0: + raise ValueError('invalid PSM (not odd)') + check = psm >> 8 + while check: + if check % 2 != 0: + raise ValueError('invalid PSM') + check >>= 8 + self.servers[psm] = server + return psm + + def register_le_coc_server( + self, + psm, + server, + max_credits=L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS, + mtu=L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU, + mps=L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS, + ): + self.check_le_coc_parameters(max_credits, mtu, mps) + + if psm == 0: + # Find a free PSM + for candidate in range( + L2CAP_LE_PSM_DYNAMIC_RANGE_START, L2CAP_LE_PSM_DYNAMIC_RANGE_END + 1 + ): + if candidate in self.le_coc_servers: + continue + psm = candidate + break + else: + raise InvalidStateError('no free PSM') + else: + # Check that the PSM isn't already in use + if psm in self.le_coc_servers: + raise ValueError('PSM already in use') + + self.le_coc_servers[psm] = ( + server, + max_credits or L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS, + mtu or L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU, + mps or L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS, + ) + + return psm + + def on_disconnection(self, connection_handle, _reason): + logger.debug(f'disconnection from {connection_handle}, cleaning up channels') + if connection_handle in self.channels: + for _, channel in self.channels[connection_handle].items(): + channel.abort() + del self.channels[connection_handle] + if connection_handle in self.le_coc_channels: + for _, channel in self.le_coc_channels[connection_handle].items(): + channel.abort() + del self.le_coc_channels[connection_handle] + if connection_handle in self.identifiers: + del self.identifiers[connection_handle] + def send_pdu(self, connection, cid, pdu): - pdu_str = pdu.hex() if type(pdu) is bytes else str(pdu) - logger.debug(f'{color(">>> Sending L2CAP PDU", "blue")} on connection [0x{connection.handle:04X}] (CID={cid}) {connection.peer_address}: {pdu_str}') + pdu_str = pdu.hex() if isinstance(pdu, bytes) else str(pdu) + logger.debug( + f'{color(">>> Sending L2CAP PDU", "blue")} ' + f'on connection [0x{connection.handle:04X}] (CID={cid}) ' + f'{connection.peer_address}: {pdu_str}' + ) self.host.send_l2cap_pdu(connection.handle, cid, bytes(pdu)) def on_pdu(self, connection, cid, pdu): - if cid == L2CAP_SIGNALING_CID or cid == L2CAP_LE_SIGNALING_CID: + if cid in (L2CAP_SIGNALING_CID, L2CAP_LE_SIGNALING_CID): # Parse the L2CAP payload into a Control Frame object control_frame = L2CAP_Control_Frame.from_bytes(pdu) self.on_control_frame(connection, cid, control_frame) + elif cid in self.fixed_channels: + self.fixed_channels[cid](connection.handle, pdu) else: if (channel := self.find_channel(connection.handle, cid)) is None: - logger.warn(color(f'channel not found for 0x{connection.handle:04X}:{cid}', 'red')) + logger.warning( + color( + f'channel not found for 0x{connection.handle:04X}:{cid}', 'red' + ) + ) return channel.on_pdu(pdu) def send_control_frame(self, connection, cid, control_frame): - logger.debug(f'{color(">>> Sending L2CAP Signaling Control Frame", "blue")} on connection [0x{connection.handle:04X}] (CID={cid}) {connection.peer_address}:\n{control_frame}') + logger.debug( + f'{color(">>> Sending L2CAP Signaling Control Frame", "blue")} ' + f'on connection [0x{connection.handle:04X}] (CID={cid}) ' + f'{connection.peer_address}:\n{control_frame}' + ) self.host.send_l2cap_pdu(connection.handle, cid, bytes(control_frame)) def on_control_frame(self, connection, cid, control_frame): - logger.debug(f'{color("<<< Received L2CAP Signaling Control Frame", "green")} on connection [0x{connection.handle:04X}] (CID={cid}) {connection.peer_address}:\n{control_frame}') + logger.debug( + f'{color("<<< Received L2CAP Signaling Control Frame", "green")} ' + f'on connection [0x{connection.handle:04X}] (CID={cid}) ' + f'{connection.peer_address}:\n{control_frame}' + ) # Find the handler method handler_name = f'on_{control_frame.name.lower()}' @@ -881,10 +1566,10 @@ class ChannelManager: connection, cid, L2CAP_Command_Reject( - identifier = control_frame.identifier, - reason = L2CAP_COMMAND_NOT_UNDERSTOOD_REASON, - data = b'' - ) + identifier=control_frame.identifier, + reason=L2CAP_COMMAND_NOT_UNDERSTOOD_REASON, + data=b'', + ), ) raise error else: @@ -893,15 +1578,14 @@ class ChannelManager: connection, cid, L2CAP_Command_Reject( - identifier = control_frame.identifier, - reason = L2CAP_COMMAND_NOT_UNDERSTOOD_REASON, - data = b'' - ) + identifier=control_frame.identifier, + reason=L2CAP_COMMAND_NOT_UNDERSTOOD_REASON, + data=b'', + ), ) - def on_l2cap_command_reject(self, connection, cid, packet): + def on_l2cap_command_reject(self, _connection, _cid, packet): logger.warning(f'{color("!!! Command rejected:", "red")} {packet.reason}') - pass def on_l2cap_connection_request(self, connection, cid, request): # Check if there's a server for this PSM @@ -915,68 +1599,117 @@ class ChannelManager: connection, cid, L2CAP_Connection_Response( - identifier = request.identifier, - destination_cid = request.source_cid, - source_cid = 0, - result = L2CAP_Connection_Response.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE, - status = 0x0000 - ) + identifier=request.identifier, + destination_cid=request.source_cid, + source_cid=0, + # pylint: disable=line-too-long + result=L2CAP_Connection_Response.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE, + status=0x0000, + ), ) return # Create a new channel - logger.debug(f'creating server channel with cid={source_cid} for psm {request.psm}') - channel = Channel(self, connection, cid, request.psm, source_cid, L2CAP_MIN_BR_EDR_MTU) + logger.debug( + f'creating server channel with cid={source_cid} for psm {request.psm}' + ) + channel = Channel( + self, connection, cid, request.psm, source_cid, L2CAP_MIN_BR_EDR_MTU + ) connection_channels[source_cid] = channel # Notify server(channel) channel.on_connection_request(request) else: - logger.warn(f'No server for connection 0x{connection.handle:04X} on PSM {request.psm}') + logger.warning( + f'No server for connection 0x{connection.handle:04X} ' + f'on PSM {request.psm}' + ) self.send_control_frame( connection, cid, L2CAP_Connection_Response( - identifier = request.identifier, - destination_cid = request.source_cid, - source_cid = 0, - result = L2CAP_Connection_Response.CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED, - status = 0x0000 - ) + identifier=request.identifier, + destination_cid=request.source_cid, + source_cid=0, + # pylint: disable=line-too-long + result=L2CAP_Connection_Response.CONNECTION_REFUSED_PSM_NOT_SUPPORTED, + status=0x0000, + ), ) def on_l2cap_connection_response(self, connection, cid, response): - if (channel := self.find_channel(connection.handle, response.source_cid)) is None: - logger.warn(color(f'channel {response.source_cid} not found for 0x{connection.handle:04X}:{cid}', 'red')) + if ( + channel := self.find_channel(connection.handle, response.source_cid) + ) is None: + logger.warning( + color( + f'channel {response.source_cid} not found for ' + f'0x{connection.handle:04X}:{cid}', + 'red', + ) + ) return channel.on_connection_response(response) def on_l2cap_configure_request(self, connection, cid, request): - if (channel := self.find_channel(connection.handle, request.destination_cid)) is None: - logger.warn(color(f'channel {request.destination_cid} not found for 0x{connection.handle:04X}:{cid}', 'red')) + if ( + channel := self.find_channel(connection.handle, request.destination_cid) + ) is None: + logger.warning( + color( + f'channel {request.destination_cid} not found for ' + f'0x{connection.handle:04X}:{cid}', + 'red', + ) + ) return channel.on_configure_request(request) def on_l2cap_configure_response(self, connection, cid, response): - if (channel := self.find_channel(connection.handle, response.source_cid)) is None: - logger.warn(color(f'channel {response.source_cid} not found for 0x{connection.handle:04X}:{cid}', 'red')) + if ( + channel := self.find_channel(connection.handle, response.source_cid) + ) is None: + logger.warning( + color( + f'channel {response.source_cid} not found for ' + f'0x{connection.handle:04X}:{cid}', + 'red', + ) + ) return channel.on_configure_response(response) def on_l2cap_disconnection_request(self, connection, cid, request): - if (channel := self.find_channel(connection.handle, request.destination_cid)) is None: - logger.warn(color(f'channel {request.destination_cid} not found for 0x{connection.handle:04X}:{cid}', 'red')) + if ( + channel := self.find_channel(connection.handle, request.destination_cid) + ) is None: + logger.warning( + color( + f'channel {request.destination_cid} not found for ' + f'0x{connection.handle:04X}:{cid}', + 'red', + ) + ) return channel.on_disconnection_request(request) def on_l2cap_disconnection_response(self, connection, cid, response): - if (channel := self.find_channel(connection.handle, response.source_cid)) is None: - logger.warn(color(f'channel {response.source_cid} not found for 0x{connection.handle:04X}:{cid}', 'red')) + if ( + channel := self.find_channel(connection.handle, response.source_cid) + ) is None: + logger.warning( + color( + f'channel {response.source_cid} not found for ' + f'0x{connection.handle:04X}:{cid}', + 'red', + ) + ) return channel.on_disconnection_response(response) @@ -986,38 +1719,35 @@ class ChannelManager: self.send_control_frame( connection, cid, - L2CAP_Echo_Response( - identifier = request.identifier, - data = request.data - ) + L2CAP_Echo_Response(identifier=request.identifier, data=request.data), ) - def on_l2cap_echo_response(self, connection, cid, response): + def on_l2cap_echo_response(self, _connection, _cid, response): logger.debug(f'<<< Echo response: data={response.data.hex()}') # TODO notify listeners def on_l2cap_information_request(self, connection, cid, request): if request.info_type == L2CAP_Information_Request.CONNECTIONLESS_MTU: result = L2CAP_Information_Response.SUCCESS - data = struct.pack('<H', 1024) # TODO: don't use a fixed value + data = self.connectionless_mtu.to_bytes(2, 'little') elif request.info_type == L2CAP_Information_Request.EXTENDED_FEATURES_SUPPORTED: result = L2CAP_Information_Response.SUCCESS - data = bytes.fromhex('00000000') # TODO: don't use a fixed value + data = sum(self.extended_features).to_bytes(4, 'little') elif request.info_type == L2CAP_Information_Request.FIXED_CHANNELS_SUPPORTED: result = L2CAP_Information_Response.SUCCESS - data = bytes.fromhex('FFFFFFFFFFFFFFFF') # TODO: don't use a fixed value + data = sum(1 << cid for cid in self.fixed_channels).to_bytes(8, 'little') else: - result = L2CAP_Information_Request.NO_SUPPORTED + result = L2CAP_Information_Response.NOT_SUPPORTED self.send_control_frame( connection, cid, L2CAP_Information_Response( - identifier = request.identifier, - info_type = request.info_type, - result = result, - data = data - ) + identifier=request.identifier, + info_type=request.info_type, + result=result, + data=data, + ), ) def on_l2cap_connection_parameter_update_request(self, connection, cid, request): @@ -1026,49 +1756,167 @@ class ChannelManager: connection, cid, L2CAP_Connection_Parameter_Update_Response( - identifier = request.identifier, - result = L2CAP_CONNECTION_PARAMETERS_ACCEPTED_RESULT + identifier=request.identifier, + result=L2CAP_CONNECTION_PARAMETERS_ACCEPTED_RESULT, + ), + ) + self.host.send_command_sync( + HCI_LE_Connection_Update_Command( + connection_handle=connection.handle, + connection_interval_min=request.interval_min, + connection_interval_max=request.interval_max, + max_latency=request.latency, + supervision_timeout=request.timeout, + min_ce_length=0, + max_ce_length=0, ) ) - self.host.send_command_sync(HCI_LE_Connection_Update_Command( - connection_handle = connection.handle, - conn_interval_min = request.interval_min, - conn_interval_max = request.interval_max, - conn_latency = request.slave_latency, - supervision_timeout = request.timeout_multiplier, - minimum_ce_length = 0, - maximum_ce_length = 0 - )) else: self.send_control_frame( connection, cid, L2CAP_Connection_Parameter_Update_Response( - identifier = request.identifier, - result = L2CAP_CONNECTION_PARAMETERS_REJECTED_RESULT - ) + identifier=request.identifier, + result=L2CAP_CONNECTION_PARAMETERS_REJECTED_RESULT, + ), ) def on_l2cap_connection_parameter_update_response(self, connection, cid, response): + # TODO: check response pass def on_l2cap_le_credit_based_connection_request(self, connection, cid, request): - # FIXME: temp fixed values - self.send_control_frame( - connection, - cid, - L2CAP_LE_Credit_Based_Connection_Response( - identifier = request.identifier, - destination_cid = 194, # FIXME: for testing only - mtu = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU, - mps = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS, - initial_credits = 3, # FIXME: for testing only - result = L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_SUCCESSFUL + if request.le_psm in self.le_coc_servers: + (server, max_credits, mtu, mps) = self.le_coc_servers[request.le_psm] + + # Check that the CID isn't already used + le_connection_channels = self.le_coc_channels.setdefault( + connection.handle, {} ) - ) + if request.source_cid in le_connection_channels: + logger.warning(f'source CID {request.source_cid} already in use') + self.send_control_frame( + connection, + cid, + L2CAP_LE_Credit_Based_Connection_Response( + identifier=request.identifier, + destination_cid=0, + mtu=mtu, + mps=mps, + initial_credits=0, + # pylint: disable=line-too-long + result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED, + ), + ) + return - def on_l2cap_le_flow_control_credit(self, connection, cid, packet): - pass + # Find a free CID for this new channel + connection_channels = self.channels.setdefault(connection.handle, {}) + source_cid = self.find_free_le_cid(connection_channels) + if source_cid is None: # Should never happen! + self.send_control_frame( + connection, + cid, + L2CAP_LE_Credit_Based_Connection_Response( + identifier=request.identifier, + destination_cid=0, + mtu=mtu, + mps=mps, + initial_credits=0, + # pylint: disable=line-too-long + result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE, + ), + ) + return + + # Create a new channel + logger.debug( + f'creating LE CoC server channel with cid={source_cid} for psm ' + f'{request.le_psm}' + ) + channel = LeConnectionOrientedChannel( + self, + connection, + request.le_psm, + source_cid, + request.source_cid, + mtu, + mps, + request.initial_credits, + request.mtu, + request.mps, + max_credits, + True, + ) + connection_channels[source_cid] = channel + le_connection_channels[request.source_cid] = channel + + # Respond + self.send_control_frame( + connection, + cid, + L2CAP_LE_Credit_Based_Connection_Response( + identifier=request.identifier, + destination_cid=source_cid, + mtu=mtu, + mps=mps, + initial_credits=max_credits, + # pylint: disable=line-too-long + result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_SUCCESSFUL, + ), + ) + + # Notify + server(channel) + else: + logger.info( + f'No LE server for connection 0x{connection.handle:04X} ' + f'on PSM {request.le_psm}' + ) + self.send_control_frame( + connection, + cid, + L2CAP_LE_Credit_Based_Connection_Response( + identifier=request.identifier, + destination_cid=0, + mtu=L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU, + mps=L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS, + initial_credits=0, + # pylint: disable=line-too-long + result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_REFUSED_LE_PSM_NOT_SUPPORTED, + ), + ) + + def on_l2cap_le_credit_based_connection_response(self, connection, _cid, response): + # Find the pending request by identifier + request = self.le_coc_requests.get(response.identifier) + if request is None: + logger.warning(color('!!! received response for unknown request', 'red')) + return + del self.le_coc_requests[response.identifier] + + # Find the channel for this request + channel = self.find_channel(connection.handle, request.source_cid) + if channel is None: + logger.warning( + color( + 'received connection response for an unknown channel ' + f'(cid={request.source_cid})', + 'red', + ) + ) + return + + # Process the response + channel.on_connection_response(response) + + def on_l2cap_le_flow_control_credit(self, connection, _cid, credit): + channel = self.find_le_coc_channel(connection.handle, credit.cid) + if channel is None: + logger.warning(f'received credits for an unknown channel (cid={credit.cid}') + return + + channel.on_credits(credit.credits) def on_channel_closed(self, channel): connection_channels = self.channels.get(channel.connection.handle) @@ -1076,22 +1924,67 @@ class ChannelManager: if channel.source_cid in connection_channels: del connection_channels[channel.source_cid] + async def open_le_coc(self, connection, psm, max_credits, mtu, mps): + self.check_le_coc_parameters(max_credits, mtu, mps) + + # Find a free CID for the new channel + connection_channels = self.channels.setdefault(connection.handle, {}) + source_cid = self.find_free_le_cid(connection_channels) + if source_cid is None: # Should never happen! + raise RuntimeError('all CIDs already in use') + + # Create the channel + logger.debug(f'creating coc channel with cid={source_cid} for psm {psm}') + channel = LeConnectionOrientedChannel( + manager=self, + connection=connection, + le_psm=psm, + source_cid=source_cid, + destination_cid=0, + mtu=mtu, + mps=mps, + credits=0, + peer_mtu=0, + peer_mps=0, + peer_credits=max_credits, + connected=False, + ) + connection_channels[source_cid] = channel + + # Connect + try: + await channel.connect() + except Exception as error: + logger.warning(f'connection failed: {error}') + del connection_channels[source_cid] + raise + + # Remember the channel by source CID and destination CID + le_connection_channels = self.le_coc_channels.setdefault(connection.handle, {}) + le_connection_channels[channel.destination_cid] = channel + + return channel + async def connect(self, connection, psm): - # NOTE: this implementation hard-codes BR/EDR more - # TODO: LE mode (maybe?) + # NOTE: this implementation hard-codes BR/EDR # Find a free CID for a new channel connection_channels = self.channels.setdefault(connection.handle, {}) - cid = self.find_free_br_edr_cid(connection_channels) - if cid is None: # Should never happen! + source_cid = self.find_free_br_edr_cid(connection_channels) + if source_cid is None: # Should never happen! raise RuntimeError('all CIDs already in use') # Create the channel - logger.debug(f'creating client channel with cid={cid} for psm {psm}') - channel = Channel(self, connection, L2CAP_SIGNALING_CID, psm, cid, L2CAP_MIN_BR_EDR_MTU) - connection_channels[cid] = channel + logger.debug(f'creating client channel with cid={source_cid} for psm {psm}') + channel = Channel( + self, connection, L2CAP_SIGNALING_CID, psm, source_cid, L2CAP_MIN_BR_EDR_MTU + ) + connection_channels[source_cid] = channel # Connect - await channel.connect() + try: + await channel.connect() + except Exception: + del connection_channels[source_cid] return channel diff --git a/bumble/link.py b/bumble/link.py index 4463e27..85ad96e 100644 --- a/bumble/link.py +++ b/bumble/link.py @@ -17,15 +17,17 @@ # ----------------------------------------------------------------------------- import logging import asyncio -import websockets from functools import partial -from colors import color +from bumble.core import BT_PERIPHERAL_ROLE, BT_BR_EDR_TRANSPORT, BT_LE_TRANSPORT +from bumble.colors import color from bumble.hci import ( Address, HCI_SUCCESS, HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR, - HCI_CONNECTION_TIMEOUT_ERROR + HCI_CONNECTION_TIMEOUT_ERROR, + HCI_PAGE_TIMEOUT_ERROR, + HCI_Connection_Complete_Event, ) # ----------------------------------------------------------------------------- @@ -47,7 +49,8 @@ def parse_parameters(params_str): # ----------------------------------------------------------------------------- -# TODO: add more support for various LL exchanges (see Vol 6, Part B - 2.4 DATA CHANNEL PDU) +# TODO: add more support for various LL exchanges +# (see Vol 6, Part B - 2.4 DATA CHANNEL PDU) # ----------------------------------------------------------------------------- class LocalLink: ''' @@ -55,8 +58,13 @@ class LocalLink: ''' def __init__(self): - self.controllers = set() + self.controllers = set() self.pending_connection = None + self.pending_classic_connection = None + + ############################################################ + # Common utils + ############################################################ def add_controller(self, controller): logger.debug(f'new controller: {controller}') @@ -71,22 +79,39 @@ class LocalLink: return controller return None - def on_address_changed(self, controller): - pass + def find_classic_controller(self, address): + for controller in self.controllers: + if controller.public_address == address: + return controller + return None def get_pending_connection(self): return self.pending_connection + ############################################################ + # LE handlers + ############################################################ + + def on_address_changed(self, controller): + pass + def send_advertising_data(self, sender_address, data): # Send the advertising data to all controllers, except the sender for controller in self.controllers: if controller.random_address != sender_address: controller.on_link_advertising_data(sender_address, data) - def send_acl_data(self, sender_address, destination_address, data): + def send_acl_data(self, sender_controller, destination_address, transport, data): # Send the data to the first controller with a matching address - if controller := self.find_controller(destination_address): - controller.on_link_acl_data(sender_address, data) + if transport == BT_LE_TRANSPORT: + destination_controller = self.find_controller(destination_address) + source_address = sender_controller.random_address + elif transport == BT_BR_EDR_TRANSPORT: + destination_controller = self.find_classic_controller(destination_address) + source_address = sender_controller.public_address + + if destination_controller is not None: + destination_controller.on_link_acl_data(source_address, transport, data) def on_connection_complete(self): # Check that we expect this call @@ -103,23 +128,31 @@ class LocalLink: return # Connect to the first controller with a matching address - if peripheral_controller := self.find_controller(le_create_connection_command.peer_address): - central_controller.on_link_peripheral_connection_complete(le_create_connection_command, HCI_SUCCESS) + if peripheral_controller := self.find_controller( + le_create_connection_command.peer_address + ): + central_controller.on_link_peripheral_connection_complete( + le_create_connection_command, HCI_SUCCESS + ) peripheral_controller.on_link_central_connected(central_address) return # No peripheral found central_controller.on_link_peripheral_connection_complete( - le_create_connection_command, - HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR + le_create_connection_command, HCI_CONNECTION_ACCEPT_TIMEOUT_ERROR ) def connect(self, central_address, le_create_connection_command): - logger.debug(f'$$$ CONNECTION {central_address} -> {le_create_connection_command.peer_address}') + logger.debug( + f'$$$ CONNECTION {central_address} -> ' + f'{le_create_connection_command.peer_address}' + ) self.pending_connection = (central_address, le_create_connection_command) asyncio.get_running_loop().call_soon(self.on_connection_complete) - def on_disconnection_complete(self, central_address, peripheral_address, disconnect_command): + def on_disconnection_complete( + self, central_address, peripheral_address, disconnect_command + ): # Find the controller that initiated the disconnection if not (central_controller := self.find_controller(central_address)): logger.warning('!!! Initiating controller not found') @@ -127,16 +160,26 @@ class LocalLink: # Disconnect from the first controller with a matching address if peripheral_controller := self.find_controller(peripheral_address): - peripheral_controller.on_link_central_disconnected(central_address, disconnect_command.reason) + peripheral_controller.on_link_central_disconnected( + central_address, disconnect_command.reason + ) - central_controller.on_link_peripheral_disconnection_complete(disconnect_command, HCI_SUCCESS) + central_controller.on_link_peripheral_disconnection_complete( + disconnect_command, HCI_SUCCESS + ) def disconnect(self, central_address, peripheral_address, disconnect_command): - logger.debug(f'$$$ DISCONNECTION {central_address} -> {peripheral_address}: reason = {disconnect_command.reason}') + logger.debug( + f'$$$ DISCONNECTION {central_address} -> ' + f'{peripheral_address}: reason = {disconnect_command.reason}' + ) args = [central_address, peripheral_address, disconnect_command] asyncio.get_running_loop().call_soon(self.on_disconnection_complete, *args) - def on_connection_encrypted(self, central_address, peripheral_address, rand, ediv, ltk): + # pylint: disable=too-many-arguments + def on_connection_encrypted( + self, central_address, peripheral_address, rand, ediv, ltk + ): logger.debug(f'*** ENCRYPTION {central_address} -> {peripheral_address}') if central_controller := self.find_controller(central_address): @@ -145,6 +188,89 @@ class LocalLink: if peripheral_controller := self.find_controller(peripheral_address): peripheral_controller.on_link_encrypted(central_address, rand, ediv, ltk) + ############################################################ + # Classic handlers + ############################################################ + + def classic_connect(self, initiator_controller, responder_address): + logger.debug( + f'[Classic] {initiator_controller.public_address} connects to {responder_address}' + ) + responder_controller = self.find_classic_controller(responder_address) + if responder_controller is None: + initiator_controller.on_classic_connection_complete( + responder_address, HCI_PAGE_TIMEOUT_ERROR + ) + return + self.pending_classic_connection = (initiator_controller, responder_controller) + + responder_controller.on_classic_connection_request( + initiator_controller.public_address, + HCI_Connection_Complete_Event.ACL_LINK_TYPE, + ) + + def classic_accept_connection( + self, responder_controller, initiator_address, responder_role + ): + logger.debug( + f'[Classic] {responder_controller.public_address} accepts to connect {initiator_address}' + ) + initiator_controller = self.find_classic_controller(initiator_address) + if initiator_controller is None: + responder_controller.on_classic_connection_complete( + responder_controller.public_address, HCI_PAGE_TIMEOUT_ERROR + ) + return + + async def task(): + if responder_role != BT_PERIPHERAL_ROLE: + initiator_controller.on_classic_role_change( + responder_controller.public_address, int(not (responder_role)) + ) + initiator_controller.on_classic_connection_complete( + responder_controller.public_address, HCI_SUCCESS + ) + + asyncio.create_task(task()) + responder_controller.on_classic_role_change( + initiator_controller.public_address, responder_role + ) + responder_controller.on_classic_connection_complete( + initiator_controller.public_address, HCI_SUCCESS + ) + self.pending_classic_connection = None + + def classic_disconnect(self, initiator_controller, responder_address, reason): + logger.debug( + f'[Classic] {initiator_controller.public_address} disconnects {responder_address}' + ) + responder_controller = self.find_classic_controller(responder_address) + + async def task(): + initiator_controller.on_classic_disconnected(responder_address, reason) + + asyncio.create_task(task()) + responder_controller.on_classic_disconnected( + initiator_controller.public_address, reason + ) + + def classic_switch_role( + self, initiator_controller, responder_address, initiator_new_role + ): + responder_controller = self.find_classic_controller(responder_address) + if responder_controller is None: + return + + async def task(): + initiator_controller.on_classic_role_change( + responder_address, initiator_new_role + ) + + asyncio.create_task(task()) + responder_controller.on_classic_role_change( + initiator_controller.public_address, int(not (initiator_new_role)) + ) + # ----------------------------------------------------------------------------- class RemoteLink: @@ -152,15 +278,18 @@ class RemoteLink: A Link implementation that communicates with other virtual controllers via a WebSocket relay ''' + def __init__(self, uri): - self.controller = None - self.uri = uri - self.execution_queue = asyncio.Queue() - self.websocket = asyncio.get_running_loop().create_future() - self.rpc_result = None - self.pending_connection = None - self.central_connections = set() # List of addresses that we have connected to - self.peripheral_connections = set() # List of addresses that have connected to us + self.controller = None + self.uri = uri + self.execution_queue = asyncio.Queue() + self.websocket = asyncio.get_running_loop().create_future() + self.rpc_result = None + self.pending_connection = None + self.central_connections = set() # List of addresses that we have connected to + self.peripheral_connections = ( + set() + ) # List of addresses that have connected to us # Connect and run asynchronously asyncio.create_task(self.run_connection()) @@ -179,6 +308,9 @@ class RemoteLink: def get_pending_connection(self): return self.pending_connection + def get_pending_classic_connection(self): + return self.pending_classic_connection + async def wait_until_connected(self): await self.websocket @@ -192,11 +324,16 @@ class RemoteLink: try: await item except Exception as error: - logger.warning(f'{color("!!! Exception in async handler:", "red")} {error}') + logger.warning( + f'{color("!!! Exception in async handler:", "red")} {error}' + ) async def run_connection(self): + import websockets # lazy import + # Connect to the relay logger.debug(f'connecting to {self.uri}') + # pylint: disable-next=no-member websocket = await websockets.connect(self.uri) self.websocket.set_result(websocket) logger.debug(f'connected to {self.uri}') @@ -227,7 +364,9 @@ class RemoteLink: self.central_connections.remove(address) if address in self.peripheral_connections: - self.controller.on_link_central_disconnected(address, HCI_CONNECTION_TIMEOUT_ERROR) + self.controller.on_link_central_disconnected( + address, HCI_CONNECTION_TIMEOUT_ERROR + ) self.peripheral_connections.remove(address) async def on_unreachable_received(self, target): @@ -244,7 +383,9 @@ class RemoteLink: async def on_advertisement_message_received(self, sender, advertisement): try: - self.controller.on_link_advertising_data(Address(sender), bytes.fromhex(advertisement)) + self.controller.on_link_advertising_data( + Address(sender), bytes.fromhex(advertisement) + ) except Exception: logger.exception('exception') @@ -263,11 +404,11 @@ class RemoteLink: self.controller.on_link_central_connected(Address(sender)) # Accept the connection by responding to it - await self.send_targetted_message(sender, 'connected') + await self.send_targeted_message(sender, 'connected') async def on_connected_message_received(self, sender, _): if not self.pending_connection: - logger.warn('received a connection ack, but no connection is pending') + logger.warning('received a connection ack, but no connection is pending') return # Remember the connection @@ -275,7 +416,9 @@ class RemoteLink: # Notify the controller logger.debug(f'connected to peripheral {self.pending_connection.peer_address}') - self.controller.on_link_peripheral_connection_complete(self.pending_connection, HCI_SUCCESS) + self.controller.on_link_peripheral_connection_complete( + self.pending_connection, HCI_SUCCESS + ) async def on_disconnect_message_received(self, sender, message): # Notify the controller @@ -287,7 +430,7 @@ class RemoteLink: if sender in self.peripheral_connections: self.peripheral_connections.remove(sender) - async def on_encrypted_message_received(self, sender, message): + async def on_encrypted_message_received(self, sender, _): # TODO parse params to get real args self.controller.on_link_encrypted(Address(sender), bytes(8), 0, bytes(16)) @@ -296,7 +439,7 @@ class RemoteLink: websocket = await self.websocket # Create a future value to hold the eventual result - assert(self.rpc_result is None) + assert self.rpc_result is None self.rpc_result = asyncio.get_running_loop().create_future() # Send the command @@ -309,7 +452,7 @@ class RemoteLink: # TODO: parse the result - async def send_targetted_message(self, target, message): + async def send_targeted_message(self, target, message): # Ensure we have a connection websocket = await self.websocket @@ -326,35 +469,62 @@ class RemoteLink: self.execute(self.notify_address_changed) async def send_advertising_data_to_relay(self, data): - await self.send_targetted_message('*', f'advertisement:{data.hex()}') + await self.send_targeted_message('*', f'advertisement:{data.hex()}') - def send_advertising_data(self, sender_address, data): + def send_advertising_data(self, _, data): self.execute(partial(self.send_advertising_data_to_relay, data)) async def send_acl_data_to_relay(self, peer_address, data): - await self.send_targetted_message(peer_address, f'acl:{data.hex()}') + await self.send_targeted_message(peer_address, f'acl:{data.hex()}') - def send_acl_data(self, sender_address, peer_address, data): + def send_acl_data(self, _, peer_address, _transport, data): + # TODO: handle different transport self.execute(partial(self.send_acl_data_to_relay, peer_address, data)) async def send_connection_request_to_relay(self, peer_address): - await self.send_targetted_message(peer_address, 'connect') + await self.send_targeted_message(peer_address, 'connect') - def connect(self, central_address, le_create_connection_command): + def connect(self, _, le_create_connection_command): if self.pending_connection: - logger.warn('connection already pending') + logger.warning('connection already pending') return self.pending_connection = le_create_connection_command - self.execute(partial(self.send_connection_request_to_relay, str(le_create_connection_command.peer_address))) + self.execute( + partial( + self.send_connection_request_to_relay, + str(le_create_connection_command.peer_address), + ) + ) def on_disconnection_complete(self, disconnect_command): - self.controller.on_link_peripheral_disconnection_complete(disconnect_command, HCI_SUCCESS) + self.controller.on_link_peripheral_disconnection_complete( + disconnect_command, HCI_SUCCESS + ) def disconnect(self, central_address, peripheral_address, disconnect_command): - logger.debug(f'disconnect {central_address} -> {peripheral_address}: reason = {disconnect_command.reason}') - self.execute(partial(self.send_targetted_message, peripheral_address, f'disconnect:reason={disconnect_command.reason}')) - asyncio.get_running_loop().call_soon(self.on_disconnection_complete, disconnect_command) + logger.debug( + f'disconnect {central_address} -> ' + f'{peripheral_address}: reason = {disconnect_command.reason}' + ) + self.execute( + partial( + self.send_targeted_message, + peripheral_address, + f'disconnect:reason={disconnect_command.reason}', + ) + ) + asyncio.get_running_loop().call_soon( + self.on_disconnection_complete, disconnect_command + ) - def on_connection_encrypted(self, central_address, peripheral_address, rand, ediv, ltk): - asyncio.get_running_loop().call_soon(self.controller.on_link_encrypted, peripheral_address, rand, ediv, ltk) - self.execute(partial(self.send_targetted_message, peripheral_address, f'encrypted:ltk={ltk.hex()}')) + def on_connection_encrypted(self, _, peripheral_address, rand, ediv, ltk): + asyncio.get_running_loop().call_soon( + self.controller.on_link_encrypted, peripheral_address, rand, ediv, ltk + ) + self.execute( + partial( + self.send_targeted_message, + peripheral_address, + f'encrypted:ltk={ltk.hex()}', + ) + ) diff --git a/bumble/pairing.py b/bumble/pairing.py new file mode 100644 index 0000000..ab356ee --- /dev/null +++ b/bumble/pairing.py @@ -0,0 +1,188 @@ +# Copyright 2021-2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import enum +from typing import Optional, Tuple + +from .hci import ( + HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY, + HCI_DISPLAY_ONLY_IO_CAPABILITY, + HCI_DISPLAY_YES_NO_IO_CAPABILITY, + HCI_KEYBOARD_ONLY_IO_CAPABILITY, +) +from .smp import ( + SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY, + SMP_KEYBOARD_ONLY_IO_CAPABILITY, + SMP_DISPLAY_ONLY_IO_CAPABILITY, + SMP_DISPLAY_YES_NO_IO_CAPABILITY, + SMP_KEYBOARD_DISPLAY_IO_CAPABILITY, + SMP_ENC_KEY_DISTRIBUTION_FLAG, + SMP_ID_KEY_DISTRIBUTION_FLAG, + SMP_SIGN_KEY_DISTRIBUTION_FLAG, + SMP_LINK_KEY_DISTRIBUTION_FLAG, +) + + +# ----------------------------------------------------------------------------- +class PairingDelegate: + """Abstract base class for Pairing Delegates.""" + + # I/O Capabilities. + # These are defined abstractly, and can be mapped to specific Classic pairing + # and/or SMP constants. + class IoCapability(enum.IntEnum): + NO_OUTPUT_NO_INPUT = SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY + KEYBOARD_INPUT_ONLY = SMP_KEYBOARD_ONLY_IO_CAPABILITY + DISPLAY_OUTPUT_ONLY = SMP_DISPLAY_ONLY_IO_CAPABILITY + DISPLAY_OUTPUT_AND_YES_NO_INPUT = SMP_DISPLAY_YES_NO_IO_CAPABILITY + DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = SMP_KEYBOARD_DISPLAY_IO_CAPABILITY + + # Direct names for backward compatibility. + NO_OUTPUT_NO_INPUT = IoCapability.NO_OUTPUT_NO_INPUT + KEYBOARD_INPUT_ONLY = IoCapability.KEYBOARD_INPUT_ONLY + DISPLAY_OUTPUT_ONLY = IoCapability.DISPLAY_OUTPUT_ONLY + DISPLAY_OUTPUT_AND_YES_NO_INPUT = IoCapability.DISPLAY_OUTPUT_AND_YES_NO_INPUT + DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = IoCapability.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT + + # Key Distribution [LE only] + class KeyDistribution(enum.IntFlag): + DISTRIBUTE_ENCRYPTION_KEY = SMP_ENC_KEY_DISTRIBUTION_FLAG + DISTRIBUTE_IDENTITY_KEY = SMP_ID_KEY_DISTRIBUTION_FLAG + DISTRIBUTE_SIGNING_KEY = SMP_SIGN_KEY_DISTRIBUTION_FLAG + DISTRIBUTE_LINK_KEY = SMP_LINK_KEY_DISTRIBUTION_FLAG + + DEFAULT_KEY_DISTRIBUTION: KeyDistribution = ( + KeyDistribution.DISTRIBUTE_ENCRYPTION_KEY + | KeyDistribution.DISTRIBUTE_IDENTITY_KEY + ) + + # Default mapping from abstract to Classic I/O capabilities. + # Subclasses may override this if they prefer a different mapping. + CLASSIC_IO_CAPABILITIES_MAP = { + NO_OUTPUT_NO_INPUT: HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY, + KEYBOARD_INPUT_ONLY: HCI_KEYBOARD_ONLY_IO_CAPABILITY, + DISPLAY_OUTPUT_ONLY: HCI_DISPLAY_ONLY_IO_CAPABILITY, + DISPLAY_OUTPUT_AND_YES_NO_INPUT: HCI_DISPLAY_YES_NO_IO_CAPABILITY, + DISPLAY_OUTPUT_AND_KEYBOARD_INPUT: HCI_DISPLAY_YES_NO_IO_CAPABILITY, + } + + io_capability: IoCapability + local_initiator_key_distribution: KeyDistribution + local_responder_key_distribution: KeyDistribution + + def __init__( + self, + io_capability: IoCapability = NO_OUTPUT_NO_INPUT, + local_initiator_key_distribution: KeyDistribution = DEFAULT_KEY_DISTRIBUTION, + local_responder_key_distribution: KeyDistribution = DEFAULT_KEY_DISTRIBUTION, + ) -> None: + self.io_capability = io_capability + self.local_initiator_key_distribution = local_initiator_key_distribution + self.local_responder_key_distribution = local_responder_key_distribution + + @property + def classic_io_capability(self) -> int: + """Map the abstract I/O capability to a Classic constant.""" + + # pylint: disable=line-too-long + return self.CLASSIC_IO_CAPABILITIES_MAP.get( + self.io_capability, HCI_NO_INPUT_NO_OUTPUT_IO_CAPABILITY + ) + + @property + def smp_io_capability(self) -> int: + """Map the abstract I/O capability to an SMP constant.""" + + # This is just a 1-1 direct mapping + return self.io_capability + + async def accept(self) -> bool: + """Accept or reject a Pairing request.""" + return True + + async def confirm(self, auto: bool = False) -> bool: + """ + Respond yes or no to a Pairing confirmation question. + The `auto` parameter stands for automatic confirmation. + """ + return True + + # pylint: disable-next=unused-argument + async def compare_numbers(self, number: int, digits: int) -> bool: + """Compare two numbers.""" + return True + + async def get_number(self) -> Optional[int]: + """ + Return an optional number as an answer to a passkey request. + Returning `None` will result in a negative reply. + """ + return 0 + + async def get_string(self, max_length: int) -> Optional[str]: + """ + Return a string whose utf-8 encoding is up to max_length bytes. + """ + return None + + # pylint: disable-next=unused-argument + async def display_number(self, number: int, digits: int) -> None: + """Display a number.""" + + # [LE only] + async def key_distribution_response( + self, peer_initiator_key_distribution: int, peer_responder_key_distribution: int + ) -> Tuple[int, int]: + """ + Return the key distribution response in an SMP protocol context. + + NOTE: since it is only used by the SMP protocol, this method's input and output + are directly as integers, using the SMP constants, rather than the abstract + KeyDistribution enums. + """ + return ( + int( + peer_initiator_key_distribution & self.local_initiator_key_distribution + ), + int( + peer_responder_key_distribution & self.local_responder_key_distribution + ), + ) + + +# ----------------------------------------------------------------------------- +class PairingConfig: + """Configuration for the Pairing protocol.""" + + def __init__( + self, + sc: bool = True, + mitm: bool = True, + bonding: bool = True, + delegate: Optional[PairingDelegate] = None, + ) -> None: + self.sc = sc + self.mitm = mitm + self.bonding = bonding + self.delegate = delegate or PairingDelegate() + + def __str__(self) -> str: + return ( + f'PairingConfig(sc={self.sc}, ' + f'mitm={self.mitm}, bonding={self.bonding}, ' + f'delegate[{self.delegate.io_capability}])' + ) diff --git a/bumble/pandora/__init__.py b/bumble/pandora/__init__.py new file mode 100644 index 0000000..e02f54a --- /dev/null +++ b/bumble/pandora/__init__.py @@ -0,0 +1,105 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Bumble Pandora server. +This module implement the Pandora Bluetooth test APIs for the Bumble stack. +""" + +__version__ = "0.0.1" + +import grpc +import grpc.aio + +from .config import Config +from .device import PandoraDevice +from .host import HostService +from .security import SecurityService, SecurityStorageService +from pandora.host_grpc_aio import add_HostServicer_to_server +from pandora.security_grpc_aio import ( + add_SecurityServicer_to_server, + add_SecurityStorageServicer_to_server, +) +from typing import Callable, List, Optional + +# public symbols +__all__ = [ + 'register_servicer_hook', + 'serve', + 'Config', + 'PandoraDevice', +] + + +# Add servicers hooks. +_SERVICERS_HOOKS: List[Callable[[PandoraDevice, Config, grpc.aio.Server], None]] = [] + + +def register_servicer_hook( + hook: Callable[[PandoraDevice, Config, grpc.aio.Server], None] +) -> None: + _SERVICERS_HOOKS.append(hook) + + +async def serve( + bumble: PandoraDevice, + config: Config = Config(), + grpc_server: Optional[grpc.aio.Server] = None, + port: int = 0, +) -> None: + # initialize a gRPC server if not provided. + server = grpc_server if grpc_server is not None else grpc.aio.server() + port = server.add_insecure_port(f'localhost:{port}') + + try: + while True: + # load server config from dict. + config.load_from_dict(bumble.config.get('server', {})) + + # add Pandora services to the gRPC server. + add_HostServicer_to_server( + HostService(server, bumble.device, config), server + ) + add_SecurityServicer_to_server( + SecurityService(bumble.device, config), server + ) + add_SecurityStorageServicer_to_server( + SecurityStorageService(bumble.device, config), server + ) + + # call hooks if any. + for hook in _SERVICERS_HOOKS: + hook(bumble, config, server) + + # open device. + await bumble.open() + try: + # Pandora require classic devices to be discoverable & connectable. + if bumble.device.classic_enabled: + await bumble.device.set_discoverable(True) + await bumble.device.set_connectable(True) + + # start & serve gRPC server. + await server.start() + await server.wait_for_termination() + finally: + # close device. + await bumble.close() + + # re-initialize the gRPC server. + server = grpc.aio.server() + server.add_insecure_port(f'localhost:{port}') + finally: + # stop server. + await server.stop(None) diff --git a/bumble/pandora/config.py b/bumble/pandora/config.py new file mode 100644 index 0000000..5edba55 --- /dev/null +++ b/bumble/pandora/config.py @@ -0,0 +1,48 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from bumble.pairing import PairingDelegate +from dataclasses import dataclass +from typing import Any, Dict + + +@dataclass +class Config: + io_capability: PairingDelegate.IoCapability = PairingDelegate.NO_OUTPUT_NO_INPUT + pairing_sc_enable: bool = True + pairing_mitm_enable: bool = True + pairing_bonding_enable: bool = True + smp_local_initiator_key_distribution: PairingDelegate.KeyDistribution = ( + PairingDelegate.DEFAULT_KEY_DISTRIBUTION + ) + smp_local_responder_key_distribution: PairingDelegate.KeyDistribution = ( + PairingDelegate.DEFAULT_KEY_DISTRIBUTION + ) + + def load_from_dict(self, config: Dict[str, Any]) -> None: + io_capability_name: str = config.get( + 'io_capability', 'no_output_no_input' + ).upper() + self.io_capability = getattr(PairingDelegate, io_capability_name) + self.pairing_sc_enable = config.get('pairing_sc_enable', True) + self.pairing_mitm_enable = config.get('pairing_mitm_enable', True) + self.pairing_bonding_enable = config.get('pairing_bonding_enable', True) + self.smp_local_initiator_key_distribution = config.get( + 'smp_local_initiator_key_distribution', + PairingDelegate.DEFAULT_KEY_DISTRIBUTION, + ) + self.smp_local_responder_key_distribution = config.get( + 'smp_local_responder_key_distribution', + PairingDelegate.DEFAULT_KEY_DISTRIBUTION, + ) diff --git a/bumble/pandora/device.py b/bumble/pandora/device.py new file mode 100644 index 0000000..a4403b6 --- /dev/null +++ b/bumble/pandora/device.py @@ -0,0 +1,157 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generic & dependency free Bumble (reference) device.""" + +from bumble import transport +from bumble.core import ( + BT_GENERIC_AUDIO_SERVICE, + BT_HANDSFREE_SERVICE, + BT_L2CAP_PROTOCOL_ID, + BT_RFCOMM_PROTOCOL_ID, +) +from bumble.device import Device, DeviceConfiguration +from bumble.host import Host +from bumble.sdp import ( + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, + SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, + DataElement, + ServiceAttribute, +) +from typing import Any, Dict, List, Optional + + +class PandoraDevice: + """ + Small wrapper around a Bumble device and it's HCI transport. + Notes: + - The Bumble device is idle by default. + - Repetitive calls to `open`/`close` will result on new Bumble device instances. + """ + + # Bumble device instance & configuration. + device: Device + config: Dict[str, Any] + + # HCI transport name & instance. + _hci_name: str + _hci: Optional[transport.Transport] # type: ignore[name-defined] + + def __init__(self, config: Dict[str, Any]) -> None: + self.config = config + self.device = _make_device(config) + self._hci_name = config.get('transport', '') + self._hci = None + + @property + def idle(self) -> bool: + return self._hci is None + + async def open(self) -> None: + if self._hci is not None: + return + + # open HCI transport & set device host. + self._hci = await transport.open_transport(self._hci_name) + self.device.host = Host(controller_source=self._hci.source, controller_sink=self._hci.sink) # type: ignore[no-untyped-call] + + # power-on. + await self.device.power_on() + + async def close(self) -> None: + if self._hci is None: + return + + # flush & re-initialize device. + await self.device.host.flush() + self.device.host = None # type: ignore[assignment] + self.device = _make_device(self.config) + + # close HCI transport. + await self._hci.close() + self._hci = None + + async def reset(self) -> None: + await self.close() + await self.open() + + def info(self) -> Optional[Dict[str, str]]: + return { + 'public_bd_address': str(self.device.public_address), + 'random_address': str(self.device.random_address), + } + + +def _make_device(config: Dict[str, Any]) -> Device: + """Initialize an idle Bumble device instance.""" + + # initialize bumble device. + device_config = DeviceConfiguration() + device_config.load_from_dict(config) + device = Device(config=device_config, host=None) + + # Add fake a2dp service to avoid Android disconnect + device.sdp_service_records = _make_sdp_records(1) + + return device + + +# TODO(b/267540823): remove when Pandora A2dp is supported +def _make_sdp_records(rfcomm_channel: int) -> Dict[int, List[ServiceAttribute]]: + return { + 0x00010001: [ + ServiceAttribute( + SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, + DataElement.unsigned_integer_32(0x00010001), + ), + ServiceAttribute( + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [ + DataElement.uuid(BT_HANDSFREE_SERVICE), + DataElement.uuid(BT_GENERIC_AUDIO_SERVICE), + ] + ), + ), + ServiceAttribute( + SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [ + DataElement.sequence([DataElement.uuid(BT_L2CAP_PROTOCOL_ID)]), + DataElement.sequence( + [ + DataElement.uuid(BT_RFCOMM_PROTOCOL_ID), + DataElement.unsigned_integer_8(rfcomm_channel), + ] + ), + ] + ), + ), + ServiceAttribute( + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [ + DataElement.sequence( + [ + DataElement.uuid(BT_HANDSFREE_SERVICE), + DataElement.unsigned_integer_16(0x0105), + ] + ) + ] + ), + ), + ] + } diff --git a/bumble/pandora/host.py b/bumble/pandora/host.py new file mode 100644 index 0000000..63b295d --- /dev/null +++ b/bumble/pandora/host.py @@ -0,0 +1,856 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import asyncio +import bumble.device +import grpc +import grpc.aio +import logging +import struct + +from . import utils +from .config import Config +from bumble.core import ( + BT_BR_EDR_TRANSPORT, + BT_LE_TRANSPORT, + BT_PERIPHERAL_ROLE, + UUID, + AdvertisingData, + ConnectionError, +) +from bumble.device import ( + DEVICE_DEFAULT_SCAN_INTERVAL, + DEVICE_DEFAULT_SCAN_WINDOW, + Advertisement, + AdvertisingType, + Device, +) +from bumble.gatt import Service +from bumble.hci import ( + HCI_CONNECTION_ALREADY_EXISTS_ERROR, + HCI_PAGE_TIMEOUT_ERROR, + HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR, + Address, +) +from google.protobuf import any_pb2, empty_pb2 # pytype: disable=pyi-error +from pandora.host_grpc_aio import HostServicer +from pandora.host_pb2 import ( + NOT_CONNECTABLE, + NOT_DISCOVERABLE, + PRIMARY_1M, + PRIMARY_CODED, + SECONDARY_1M, + SECONDARY_2M, + SECONDARY_CODED, + SECONDARY_NONE, + AdvertiseRequest, + AdvertiseResponse, + Connection, + ConnectLERequest, + ConnectLEResponse, + ConnectRequest, + ConnectResponse, + DataTypes, + DisconnectRequest, + InquiryResponse, + PrimaryPhy, + ReadLocalAddressResponse, + ScanningResponse, + ScanRequest, + SecondaryPhy, + SetConnectabilityModeRequest, + SetDiscoverabilityModeRequest, + WaitConnectionRequest, + WaitConnectionResponse, + WaitDisconnectionRequest, +) +from typing import AsyncGenerator, Dict, List, Optional, Set, Tuple, cast + +PRIMARY_PHY_MAP: Dict[int, PrimaryPhy] = { + # Default value reported by Bumble for legacy Advertising reports. + # FIXME(uael): `None` might be a better value, but Bumble need to change accordingly. + 0: PRIMARY_1M, + 1: PRIMARY_1M, + 3: PRIMARY_CODED, +} + +SECONDARY_PHY_MAP: Dict[int, SecondaryPhy] = { + 0: SECONDARY_NONE, + 1: SECONDARY_1M, + 2: SECONDARY_2M, + 3: SECONDARY_CODED, +} + + +class HostService(HostServicer): + waited_connections: Set[int] + + def __init__( + self, grpc_server: grpc.aio.Server, device: Device, config: Config + ) -> None: + self.log = utils.BumbleServerLoggerAdapter( + logging.getLogger(), {'service_name': 'Host', 'device': device} + ) + self.grpc_server = grpc_server + self.device = device + self.config = config + self.waited_connections = set() + + @utils.rpc + async def FactoryReset( + self, request: empty_pb2.Empty, context: grpc.ServicerContext + ) -> empty_pb2.Empty: + self.log.info('FactoryReset') + + # delete all bonds + if self.device.keystore is not None: + await self.device.keystore.delete_all() + + # trigger gRCP server stop then return + asyncio.create_task(self.grpc_server.stop(None)) + return empty_pb2.Empty() + + @utils.rpc + async def Reset( + self, request: empty_pb2.Empty, context: grpc.ServicerContext + ) -> empty_pb2.Empty: + self.log.info('Reset') + + # clear service. + self.waited_connections.clear() + + # (re) power device on + await self.device.power_on() + return empty_pb2.Empty() + + @utils.rpc + async def ReadLocalAddress( + self, request: empty_pb2.Empty, context: grpc.ServicerContext + ) -> ReadLocalAddressResponse: + self.log.info('ReadLocalAddress') + return ReadLocalAddressResponse( + address=bytes(reversed(bytes(self.device.public_address))) + ) + + @utils.rpc + async def Connect( + self, request: ConnectRequest, context: grpc.ServicerContext + ) -> ConnectResponse: + # Need to reverse bytes order since Bumble Address is using MSB. + address = Address( + bytes(reversed(request.address)), address_type=Address.PUBLIC_DEVICE_ADDRESS + ) + self.log.info(f"Connect to {address}") + + try: + connection = await self.device.connect( + address, transport=BT_BR_EDR_TRANSPORT + ) + except ConnectionError as e: + if e.error_code == HCI_PAGE_TIMEOUT_ERROR: + self.log.warning(f"Peer not found: {e}") + return ConnectResponse(peer_not_found=empty_pb2.Empty()) + if e.error_code == HCI_CONNECTION_ALREADY_EXISTS_ERROR: + self.log.warning(f"Connection already exists: {e}") + return ConnectResponse(connection_already_exists=empty_pb2.Empty()) + raise e + + self.log.info(f"Connect to {address} done (handle={connection.handle})") + + cookie = any_pb2.Any(value=connection.handle.to_bytes(4, 'big')) + return ConnectResponse(connection=Connection(cookie=cookie)) + + @utils.rpc + async def WaitConnection( + self, request: WaitConnectionRequest, context: grpc.ServicerContext + ) -> WaitConnectionResponse: + if not request.address: + raise ValueError('Request address field must be set') + + # Need to reverse bytes order since Bumble Address is using MSB. + address = Address( + bytes(reversed(request.address)), address_type=Address.PUBLIC_DEVICE_ADDRESS + ) + if address in (Address.NIL, Address.ANY): + raise ValueError('Invalid address') + + self.log.info(f"WaitConnection from {address}...") + + connection = self.device.find_connection_by_bd_addr( + address, transport=BT_BR_EDR_TRANSPORT + ) + if connection and id(connection) in self.waited_connections: + # this connection was already returned: wait for a new one. + connection = None + + if not connection: + connection = await self.device.accept(address) + + # save connection has waited and respond. + self.waited_connections.add(id(connection)) + + self.log.info( + f"WaitConnection from {address} done (handle={connection.handle})" + ) + + cookie = any_pb2.Any(value=connection.handle.to_bytes(4, 'big')) + return WaitConnectionResponse(connection=Connection(cookie=cookie)) + + @utils.rpc + async def ConnectLE( + self, request: ConnectLERequest, context: grpc.ServicerContext + ) -> ConnectLEResponse: + address = utils.address_from_request(request, request.WhichOneof("address")) + if address in (Address.NIL, Address.ANY): + raise ValueError('Invalid address') + + self.log.info(f"ConnectLE to {address}...") + + try: + connection = await self.device.connect( + address, + transport=BT_LE_TRANSPORT, + own_address_type=request.own_address_type, + ) + except ConnectionError as e: + if e.error_code == HCI_PAGE_TIMEOUT_ERROR: + self.log.warning(f"Peer not found: {e}") + return ConnectLEResponse(peer_not_found=empty_pb2.Empty()) + if e.error_code == HCI_CONNECTION_ALREADY_EXISTS_ERROR: + self.log.warning(f"Connection already exists: {e}") + return ConnectLEResponse(connection_already_exists=empty_pb2.Empty()) + raise e + + self.log.info(f"ConnectLE to {address} done (handle={connection.handle})") + + cookie = any_pb2.Any(value=connection.handle.to_bytes(4, 'big')) + return ConnectLEResponse(connection=Connection(cookie=cookie)) + + @utils.rpc + async def Disconnect( + self, request: DisconnectRequest, context: grpc.ServicerContext + ) -> empty_pb2.Empty: + connection_handle = int.from_bytes(request.connection.cookie.value, 'big') + self.log.info(f"Disconnect: {connection_handle}") + + self.log.info("Disconnecting...") + if connection := self.device.lookup_connection(connection_handle): + await connection.disconnect(HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR) + self.log.info("Disconnected") + + return empty_pb2.Empty() + + @utils.rpc + async def WaitDisconnection( + self, request: WaitDisconnectionRequest, context: grpc.ServicerContext + ) -> empty_pb2.Empty: + connection_handle = int.from_bytes(request.connection.cookie.value, 'big') + self.log.info(f"WaitDisconnection: {connection_handle}") + + if connection := self.device.lookup_connection(connection_handle): + disconnection_future: asyncio.Future[ + None + ] = asyncio.get_running_loop().create_future() + + def on_disconnection(_: None) -> None: + disconnection_future.set_result(None) + + connection.on('disconnection', on_disconnection) + try: + await disconnection_future + self.log.info("Disconnected") + finally: + connection.remove_listener('disconnection', on_disconnection) # type: ignore + + return empty_pb2.Empty() + + @utils.rpc + async def Advertise( + self, request: AdvertiseRequest, context: grpc.ServicerContext + ) -> AsyncGenerator[AdvertiseResponse, None]: + if not request.legacy: + raise NotImplementedError( + "TODO: add support for extended advertising in Bumble" + ) + if request.interval: + raise NotImplementedError("TODO: add support for `request.interval`") + if request.interval_range: + raise NotImplementedError("TODO: add support for `request.interval_range`") + if request.primary_phy: + raise NotImplementedError("TODO: add support for `request.primary_phy`") + if request.secondary_phy: + raise NotImplementedError("TODO: add support for `request.secondary_phy`") + + if self.device.is_advertising: + raise NotImplementedError('TODO: add support for advertising sets') + + if data := request.data: + self.device.advertising_data = bytes(self.unpack_data_types(data)) + + if scan_response_data := request.scan_response_data: + self.device.scan_response_data = bytes( + self.unpack_data_types(scan_response_data) + ) + scannable = True + else: + scannable = False + + # Retrieve services data + for service in self.device.gatt_server.attributes: + if isinstance(service, Service) and ( + service_data := service.get_advertising_data() + ): + service_uuid = service.uuid.to_hex_str('-') + if ( + service_uuid in request.data.incomplete_service_class_uuids16 + or service_uuid in request.data.complete_service_class_uuids16 + or service_uuid in request.data.incomplete_service_class_uuids32 + or service_uuid in request.data.complete_service_class_uuids32 + or service_uuid + in request.data.incomplete_service_class_uuids128 + or service_uuid in request.data.complete_service_class_uuids128 + ): + self.device.advertising_data += service_data + if ( + service_uuid + in scan_response_data.incomplete_service_class_uuids16 + or service_uuid + in scan_response_data.complete_service_class_uuids16 + or service_uuid + in scan_response_data.incomplete_service_class_uuids32 + or service_uuid + in scan_response_data.complete_service_class_uuids32 + or service_uuid + in scan_response_data.incomplete_service_class_uuids128 + or service_uuid + in scan_response_data.complete_service_class_uuids128 + ): + self.device.scan_response_data += service_data + + target = None + if request.connectable and scannable: + advertising_type = AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE + elif scannable: + advertising_type = AdvertisingType.UNDIRECTED_SCANNABLE + else: + advertising_type = AdvertisingType.UNDIRECTED + else: + target = None + advertising_type = AdvertisingType.UNDIRECTED + + if request.target: + # Need to reverse bytes order since Bumble Address is using MSB. + target_bytes = bytes(reversed(request.target)) + if request.target_variant() == "public": + target = Address(target_bytes, Address.PUBLIC_DEVICE_ADDRESS) + advertising_type = ( + AdvertisingType.DIRECTED_CONNECTABLE_HIGH_DUTY + ) # FIXME: HIGH_DUTY ? + else: + target = Address(target_bytes, Address.RANDOM_DEVICE_ADDRESS) + advertising_type = ( + AdvertisingType.DIRECTED_CONNECTABLE_HIGH_DUTY + ) # FIXME: HIGH_DUTY ? + + if request.connectable: + + def on_connection(connection: bumble.device.Connection) -> None: + if ( + connection.transport == BT_LE_TRANSPORT + and connection.role == BT_PERIPHERAL_ROLE + ): + pending_connection.set_result(connection) + + self.device.on('connection', on_connection) + + try: + while True: + if not self.device.is_advertising: + self.log.info('Advertise') + await self.device.start_advertising( + target=target, + advertising_type=advertising_type, + own_address_type=request.own_address_type, + ) + + if not request.connectable: + await asyncio.sleep(1) + continue + + pending_connection: asyncio.Future[ + bumble.device.Connection + ] = asyncio.get_running_loop().create_future() + + self.log.info('Wait for LE connection...') + connection = await pending_connection + + self.log.info( + f"Advertise: Connected to {connection.peer_address} (handle={connection.handle})" + ) + + cookie = any_pb2.Any(value=connection.handle.to_bytes(4, 'big')) + yield AdvertiseResponse(connection=Connection(cookie=cookie)) + + # wait a small delay before restarting the advertisement. + await asyncio.sleep(1) + finally: + if request.connectable: + self.device.remove_listener('connection', on_connection) # type: ignore + + try: + self.log.info('Stop advertising') + await self.device.abort_on('flush', self.device.stop_advertising()) + except: + pass + + @utils.rpc + async def Scan( + self, request: ScanRequest, context: grpc.ServicerContext + ) -> AsyncGenerator[ScanningResponse, None]: + # TODO: modify `start_scanning` to accept floats instead of int for ms values + if request.phys: + raise NotImplementedError("TODO: add support for `request.phys`") + + self.log.info('Scan') + + scan_queue: asyncio.Queue[Advertisement] = asyncio.Queue() + handler = self.device.on('advertisement', scan_queue.put_nowait) + await self.device.start_scanning( + legacy=request.legacy, + active=not request.passive, + own_address_type=request.own_address_type, + scan_interval=int(request.interval) + if request.interval + else DEVICE_DEFAULT_SCAN_INTERVAL, + scan_window=int(request.window) + if request.window + else DEVICE_DEFAULT_SCAN_WINDOW, + ) + + try: + # TODO: add support for `direct_address` in Bumble + # TODO: add support for `periodic_advertising_interval` in Bumble + while adv := await scan_queue.get(): + sr = ScanningResponse( + legacy=adv.is_legacy, + connectable=adv.is_connectable, + scannable=adv.is_scannable, + truncated=adv.is_truncated, + sid=adv.sid, + primary_phy=PRIMARY_PHY_MAP[adv.primary_phy], + secondary_phy=SECONDARY_PHY_MAP[adv.secondary_phy], + tx_power=adv.tx_power, + rssi=adv.rssi, + data=self.pack_data_types(adv.data), + ) + + if adv.address.address_type == Address.PUBLIC_DEVICE_ADDRESS: + sr.public = bytes(reversed(bytes(adv.address))) + elif adv.address.address_type == Address.RANDOM_DEVICE_ADDRESS: + sr.random = bytes(reversed(bytes(adv.address))) + elif adv.address.address_type == Address.PUBLIC_IDENTITY_ADDRESS: + sr.public_identity = bytes(reversed(bytes(adv.address))) + else: + sr.random_static_identity = bytes(reversed(bytes(adv.address))) + + yield sr + + finally: + self.device.remove_listener('advertisement', handler) # type: ignore + try: + self.log.info('Stop scanning') + await self.device.abort_on('flush', self.device.stop_scanning()) + except: + pass + + @utils.rpc + async def Inquiry( + self, request: empty_pb2.Empty, context: grpc.ServicerContext + ) -> AsyncGenerator[InquiryResponse, None]: + self.log.info('Inquiry') + + inquiry_queue: asyncio.Queue[ + Optional[Tuple[Address, int, AdvertisingData, int]] + ] = asyncio.Queue() + complete_handler = self.device.on( + 'inquiry_complete', lambda: inquiry_queue.put_nowait(None) + ) + result_handler = self.device.on( # type: ignore + 'inquiry_result', + lambda address, class_of_device, eir_data, rssi: inquiry_queue.put_nowait( # type: ignore + (address, class_of_device, eir_data, rssi) # type: ignore + ), + ) + + await self.device.start_discovery(auto_restart=False) + try: + while inquiry_result := await inquiry_queue.get(): + (address, class_of_device, eir_data, rssi) = inquiry_result + # FIXME: if needed, add support for `page_scan_repetition_mode` and `clock_offset` in Bumble + yield InquiryResponse( + address=bytes(reversed(bytes(address))), + class_of_device=class_of_device, + rssi=rssi, + data=self.pack_data_types(eir_data), + ) + + finally: + self.device.remove_listener('inquiry_complete', complete_handler) # type: ignore + self.device.remove_listener('inquiry_result', result_handler) # type: ignore + try: + self.log.info('Stop inquiry') + await self.device.abort_on('flush', self.device.stop_discovery()) + except: + pass + + @utils.rpc + async def SetDiscoverabilityMode( + self, request: SetDiscoverabilityModeRequest, context: grpc.ServicerContext + ) -> empty_pb2.Empty: + self.log.info("SetDiscoverabilityMode") + await self.device.set_discoverable(request.mode != NOT_DISCOVERABLE) + return empty_pb2.Empty() + + @utils.rpc + async def SetConnectabilityMode( + self, request: SetConnectabilityModeRequest, context: grpc.ServicerContext + ) -> empty_pb2.Empty: + self.log.info("SetConnectabilityMode") + await self.device.set_connectable(request.mode != NOT_CONNECTABLE) + return empty_pb2.Empty() + + def unpack_data_types(self, dt: DataTypes) -> AdvertisingData: + ad_structures: List[Tuple[int, bytes]] = [] + + uuids: List[str] + datas: Dict[str, bytes] + + def uuid128_from_str(uuid: str) -> bytes: + """Decode a 128-bit uuid encoded as XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + to byte format.""" + return bytes(reversed(bytes.fromhex(uuid.replace('-', '')))) + + def uuid32_from_str(uuid: str) -> bytes: + """Decode a 32-bit uuid encoded as XXXXXXXX to byte format.""" + return bytes(reversed(bytes.fromhex(uuid))) + + def uuid16_from_str(uuid: str) -> bytes: + """Decode a 16-bit uuid encoded as XXXX to byte format.""" + return bytes(reversed(bytes.fromhex(uuid))) + + if uuids := dt.incomplete_service_class_uuids16: + ad_structures.append( + ( + AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, + b''.join([uuid16_from_str(uuid) for uuid in uuids]), + ) + ) + if uuids := dt.complete_service_class_uuids16: + ad_structures.append( + ( + AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, + b''.join([uuid16_from_str(uuid) for uuid in uuids]), + ) + ) + if uuids := dt.incomplete_service_class_uuids32: + ad_structures.append( + ( + AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS, + b''.join([uuid32_from_str(uuid) for uuid in uuids]), + ) + ) + if uuids := dt.complete_service_class_uuids32: + ad_structures.append( + ( + AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS, + b''.join([uuid32_from_str(uuid) for uuid in uuids]), + ) + ) + if uuids := dt.incomplete_service_class_uuids128: + ad_structures.append( + ( + AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, + b''.join([uuid128_from_str(uuid) for uuid in uuids]), + ) + ) + if uuids := dt.complete_service_class_uuids128: + ad_structures.append( + ( + AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, + b''.join([uuid128_from_str(uuid) for uuid in uuids]), + ) + ) + if dt.HasField('include_shortened_local_name'): + ad_structures.append( + ( + AdvertisingData.SHORTENED_LOCAL_NAME, + bytes(self.device.name[:8], 'utf-8'), + ) + ) + elif dt.shortened_local_name: + ad_structures.append( + ( + AdvertisingData.SHORTENED_LOCAL_NAME, + bytes(dt.shortened_local_name, 'utf-8'), + ) + ) + if dt.HasField('include_complete_local_name'): + ad_structures.append( + (AdvertisingData.COMPLETE_LOCAL_NAME, bytes(self.device.name, 'utf-8')) + ) + elif dt.complete_local_name: + ad_structures.append( + ( + AdvertisingData.COMPLETE_LOCAL_NAME, + bytes(dt.complete_local_name, 'utf-8'), + ) + ) + if dt.HasField('include_tx_power_level'): + raise ValueError('unsupported data type') + elif dt.tx_power_level: + ad_structures.append( + ( + AdvertisingData.TX_POWER_LEVEL, + bytes(struct.pack('<I', dt.tx_power_level)[:1]), + ) + ) + if dt.HasField('include_class_of_device'): + ad_structures.append( + ( + AdvertisingData.CLASS_OF_DEVICE, + bytes(struct.pack('<I', self.device.class_of_device)[:-1]), + ) + ) + elif dt.class_of_device: + ad_structures.append( + ( + AdvertisingData.CLASS_OF_DEVICE, + bytes(struct.pack('<I', dt.class_of_device)[:-1]), + ) + ) + if dt.peripheral_connection_interval_min: + ad_structures.append( + ( + AdvertisingData.PERIPHERAL_CONNECTION_INTERVAL_RANGE, + bytes( + [ + *struct.pack('<H', dt.peripheral_connection_interval_min), + *struct.pack( + '<H', + dt.peripheral_connection_interval_max + if dt.peripheral_connection_interval_max + else dt.peripheral_connection_interval_min, + ), + ] + ), + ) + ) + if uuids := dt.service_solicitation_uuids16: + ad_structures.append( + ( + AdvertisingData.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS, + b''.join([uuid16_from_str(uuid) for uuid in uuids]), + ) + ) + if uuids := dt.service_solicitation_uuids32: + ad_structures.append( + ( + AdvertisingData.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS, + b''.join([uuid32_from_str(uuid) for uuid in uuids]), + ) + ) + if uuids := dt.service_solicitation_uuids128: + ad_structures.append( + ( + AdvertisingData.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS, + b''.join([uuid128_from_str(uuid) for uuid in uuids]), + ) + ) + if datas := dt.service_data_uuid16: + ad_structures.extend( + [ + ( + AdvertisingData.SERVICE_DATA_16_BIT_UUID, + uuid16_from_str(uuid) + data, + ) + for uuid, data in datas.items() + ] + ) + if datas := dt.service_data_uuid32: + ad_structures.extend( + [ + ( + AdvertisingData.SERVICE_DATA_32_BIT_UUID, + uuid32_from_str(uuid) + data, + ) + for uuid, data in datas.items() + ] + ) + if datas := dt.service_data_uuid128: + ad_structures.extend( + [ + ( + AdvertisingData.SERVICE_DATA_128_BIT_UUID, + uuid128_from_str(uuid) + data, + ) + for uuid, data in datas.items() + ] + ) + if dt.appearance: + ad_structures.append( + (AdvertisingData.APPEARANCE, struct.pack('<H', dt.appearance)) + ) + if dt.advertising_interval: + ad_structures.append( + ( + AdvertisingData.ADVERTISING_INTERVAL, + struct.pack('<H', dt.advertising_interval), + ) + ) + if dt.uri: + ad_structures.append((AdvertisingData.URI, bytes(dt.uri, 'utf-8'))) + if dt.le_supported_features: + ad_structures.append( + (AdvertisingData.LE_SUPPORTED_FEATURES, dt.le_supported_features) + ) + if dt.manufacturer_specific_data: + ad_structures.append( + ( + AdvertisingData.MANUFACTURER_SPECIFIC_DATA, + dt.manufacturer_specific_data, + ) + ) + + return AdvertisingData(ad_structures) + + def pack_data_types(self, ad: AdvertisingData) -> DataTypes: + dt = DataTypes() + uuids: List[UUID] + s: str + i: int + ij: Tuple[int, int] + uuid_data: Tuple[UUID, bytes] + data: bytes + + if uuids := cast( + List[UUID], + ad.get(AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS), + ): + dt.incomplete_service_class_uuids16.extend( + list(map(lambda x: x.to_hex_str('-'), uuids)) + ) + if uuids := cast( + List[UUID], + ad.get(AdvertisingData.COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS), + ): + dt.complete_service_class_uuids16.extend( + list(map(lambda x: x.to_hex_str('-'), uuids)) + ) + if uuids := cast( + List[UUID], + ad.get(AdvertisingData.INCOMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS), + ): + dt.incomplete_service_class_uuids32.extend( + list(map(lambda x: x.to_hex_str('-'), uuids)) + ) + if uuids := cast( + List[UUID], + ad.get(AdvertisingData.COMPLETE_LIST_OF_32_BIT_SERVICE_CLASS_UUIDS), + ): + dt.complete_service_class_uuids32.extend( + list(map(lambda x: x.to_hex_str('-'), uuids)) + ) + if uuids := cast( + List[UUID], + ad.get(AdvertisingData.INCOMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS), + ): + dt.incomplete_service_class_uuids128.extend( + list(map(lambda x: x.to_hex_str('-'), uuids)) + ) + if uuids := cast( + List[UUID], + ad.get(AdvertisingData.COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS), + ): + dt.complete_service_class_uuids128.extend( + list(map(lambda x: x.to_hex_str('-'), uuids)) + ) + if s := cast(str, ad.get(AdvertisingData.SHORTENED_LOCAL_NAME)): + dt.shortened_local_name = s + if s := cast(str, ad.get(AdvertisingData.COMPLETE_LOCAL_NAME)): + dt.complete_local_name = s + if i := cast(int, ad.get(AdvertisingData.TX_POWER_LEVEL)): + dt.tx_power_level = i + if i := cast(int, ad.get(AdvertisingData.CLASS_OF_DEVICE)): + dt.class_of_device = i + if ij := cast( + Tuple[int, int], + ad.get(AdvertisingData.PERIPHERAL_CONNECTION_INTERVAL_RANGE), + ): + dt.peripheral_connection_interval_min = ij[0] + dt.peripheral_connection_interval_max = ij[1] + if uuids := cast( + List[UUID], + ad.get(AdvertisingData.LIST_OF_16_BIT_SERVICE_SOLICITATION_UUIDS), + ): + dt.service_solicitation_uuids16.extend( + list(map(lambda x: x.to_hex_str('-'), uuids)) + ) + if uuids := cast( + List[UUID], + ad.get(AdvertisingData.LIST_OF_32_BIT_SERVICE_SOLICITATION_UUIDS), + ): + dt.service_solicitation_uuids32.extend( + list(map(lambda x: x.to_hex_str('-'), uuids)) + ) + if uuids := cast( + List[UUID], + ad.get(AdvertisingData.LIST_OF_128_BIT_SERVICE_SOLICITATION_UUIDS), + ): + dt.service_solicitation_uuids128.extend( + list(map(lambda x: x.to_hex_str('-'), uuids)) + ) + if uuid_data := cast( + Tuple[UUID, bytes], ad.get(AdvertisingData.SERVICE_DATA_16_BIT_UUID) + ): + dt.service_data_uuid16[uuid_data[0].to_hex_str('-')] = uuid_data[1] + if uuid_data := cast( + Tuple[UUID, bytes], ad.get(AdvertisingData.SERVICE_DATA_32_BIT_UUID) + ): + dt.service_data_uuid32[uuid_data[0].to_hex_str('-')] = uuid_data[1] + if uuid_data := cast( + Tuple[UUID, bytes], ad.get(AdvertisingData.SERVICE_DATA_128_BIT_UUID) + ): + dt.service_data_uuid128[uuid_data[0].to_hex_str('-')] = uuid_data[1] + if data := cast(bytes, ad.get(AdvertisingData.PUBLIC_TARGET_ADDRESS, raw=True)): + dt.public_target_addresses.extend( + [data[i * 6 :: i * 6 + 6] for i in range(int(len(data) / 6))] + ) + if data := cast(bytes, ad.get(AdvertisingData.RANDOM_TARGET_ADDRESS, raw=True)): + dt.random_target_addresses.extend( + [data[i * 6 :: i * 6 + 6] for i in range(int(len(data) / 6))] + ) + if i := cast(int, ad.get(AdvertisingData.APPEARANCE)): + dt.appearance = i + if i := cast(int, ad.get(AdvertisingData.ADVERTISING_INTERVAL)): + dt.advertising_interval = i + if s := cast(str, ad.get(AdvertisingData.URI)): + dt.uri = s + if data := cast(bytes, ad.get(AdvertisingData.LE_SUPPORTED_FEATURES, raw=True)): + dt.le_supported_features = data + if data := cast( + bytes, ad.get(AdvertisingData.MANUFACTURER_SPECIFIC_DATA, raw=True) + ): + dt.manufacturer_specific_data = data + + return dt diff --git a/bumble/pandora/py.typed b/bumble/pandora/py.typed new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bumble/pandora/py.typed diff --git a/bumble/pandora/security.py b/bumble/pandora/security.py new file mode 100644 index 0000000..fee1b7a --- /dev/null +++ b/bumble/pandora/security.py @@ -0,0 +1,529 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import asyncio +import grpc +import logging + +from . import utils +from .config import Config +from bumble import hci +from bumble.core import ( + BT_BR_EDR_TRANSPORT, + BT_LE_TRANSPORT, + BT_PERIPHERAL_ROLE, + ProtocolError, +) +from bumble.device import Connection as BumbleConnection, Device +from bumble.hci import HCI_Error +from bumble.pairing import PairingConfig, PairingDelegate as BasePairingDelegate +from contextlib import suppress +from google.protobuf import ( + any_pb2, + empty_pb2, + wrappers_pb2, +) # pytype: disable=pyi-error +from google.protobuf.wrappers_pb2 import BoolValue # pytype: disable=pyi-error +from pandora.host_pb2 import Connection +from pandora.security_grpc_aio import SecurityServicer, SecurityStorageServicer +from pandora.security_pb2 import ( + LE_LEVEL1, + LE_LEVEL2, + LE_LEVEL3, + LE_LEVEL4, + LEVEL0, + LEVEL1, + LEVEL2, + LEVEL3, + LEVEL4, + DeleteBondRequest, + IsBondedRequest, + LESecurityLevel, + PairingEvent, + PairingEventAnswer, + SecureRequest, + SecureResponse, + SecurityLevel, + WaitSecurityRequest, + WaitSecurityResponse, +) +from typing import Any, AsyncGenerator, AsyncIterator, Callable, Dict, Optional, Union + + +class PairingDelegate(BasePairingDelegate): + def __init__( + self, + connection: BumbleConnection, + service: "SecurityService", + io_capability: BasePairingDelegate.IoCapability = BasePairingDelegate.NO_OUTPUT_NO_INPUT, + local_initiator_key_distribution: BasePairingDelegate.KeyDistribution = BasePairingDelegate.DEFAULT_KEY_DISTRIBUTION, + local_responder_key_distribution: BasePairingDelegate.KeyDistribution = BasePairingDelegate.DEFAULT_KEY_DISTRIBUTION, + ) -> None: + self.log = utils.BumbleServerLoggerAdapter( + logging.getLogger(), + {'service_name': 'Security', 'device': connection.device}, + ) + self.connection = connection + self.service = service + super().__init__( + io_capability, + local_initiator_key_distribution, + local_responder_key_distribution, + ) + + async def accept(self) -> bool: + return True + + def add_origin(self, ev: PairingEvent) -> PairingEvent: + if not self.connection.is_incomplete: + assert ev.connection + ev.connection.CopyFrom( + Connection( + cookie=any_pb2.Any(value=self.connection.handle.to_bytes(4, 'big')) + ) + ) + else: + # In BR/EDR, connection may not be complete, + # use address instead + assert self.connection.transport == BT_BR_EDR_TRANSPORT + ev.address = bytes(reversed(bytes(self.connection.peer_address))) + + return ev + + async def confirm(self, auto: bool = False) -> bool: + self.log.info( + f"Pairing event: `just_works` (io_capability: {self.io_capability})" + ) + + if self.service.event_queue is None or self.service.event_answer is None: + return True + + event = self.add_origin(PairingEvent(just_works=empty_pb2.Empty())) + self.service.event_queue.put_nowait(event) + answer = await anext(self.service.event_answer) # pytype: disable=name-error + assert answer.event == event + assert answer.answer_variant() == 'confirm' and answer.confirm is not None + return answer.confirm + + async def compare_numbers(self, number: int, digits: int = 6) -> bool: + self.log.info( + f"Pairing event: `numeric_comparison` (io_capability: {self.io_capability})" + ) + + if self.service.event_queue is None or self.service.event_answer is None: + raise RuntimeError('security: unhandled number comparison request') + + event = self.add_origin(PairingEvent(numeric_comparison=number)) + self.service.event_queue.put_nowait(event) + answer = await anext(self.service.event_answer) # pytype: disable=name-error + assert answer.event == event + assert answer.answer_variant() == 'confirm' and answer.confirm is not None + return answer.confirm + + async def get_number(self) -> Optional[int]: + self.log.info( + f"Pairing event: `passkey_entry_request` (io_capability: {self.io_capability})" + ) + + if self.service.event_queue is None or self.service.event_answer is None: + raise RuntimeError('security: unhandled number request') + + event = self.add_origin(PairingEvent(passkey_entry_request=empty_pb2.Empty())) + self.service.event_queue.put_nowait(event) + answer = await anext(self.service.event_answer) # pytype: disable=name-error + assert answer.event == event + if answer.answer_variant() is None: + return None + assert answer.answer_variant() == 'passkey' + return answer.passkey + + async def get_string(self, max_length: int) -> Optional[str]: + self.log.info( + f"Pairing event: `pin_code_request` (io_capability: {self.io_capability})" + ) + + if self.service.event_queue is None or self.service.event_answer is None: + raise RuntimeError('security: unhandled pin_code request') + + event = self.add_origin(PairingEvent(pin_code_request=empty_pb2.Empty())) + self.service.event_queue.put_nowait(event) + answer = await anext(self.service.event_answer) # pytype: disable=name-error + assert answer.event == event + if answer.answer_variant() is None: + return None + assert answer.answer_variant() == 'pin' + + if answer.pin is None: + return None + + pin = answer.pin.decode('utf-8') + if not pin or len(pin) > max_length: + raise ValueError(f'Pin must be utf-8 encoded up to {max_length} bytes') + + return pin + + async def display_number(self, number: int, digits: int = 6) -> None: + if ( + self.connection.transport == BT_BR_EDR_TRANSPORT + and self.io_capability == BasePairingDelegate.DISPLAY_OUTPUT_ONLY + ): + return + + self.log.info( + f"Pairing event: `passkey_entry_notification` (io_capability: {self.io_capability})" + ) + + if self.service.event_queue is None: + raise RuntimeError('security: unhandled number display request') + + event = self.add_origin(PairingEvent(passkey_entry_notification=number)) + self.service.event_queue.put_nowait(event) + + +BR_LEVEL_REACHED: Dict[SecurityLevel, Callable[[BumbleConnection], bool]] = { + LEVEL0: lambda connection: True, + LEVEL1: lambda connection: connection.encryption == 0 or connection.authenticated, + LEVEL2: lambda connection: connection.encryption != 0 and connection.authenticated, + LEVEL3: lambda connection: connection.encryption != 0 + and connection.authenticated + and connection.link_key_type + in ( + hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_192_TYPE, + hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE, + ), + LEVEL4: lambda connection: connection.encryption + == hci.HCI_Encryption_Change_Event.AES_CCM + and connection.authenticated + and connection.link_key_type + == hci.HCI_AUTHENTICATED_COMBINATION_KEY_GENERATED_FROM_P_256_TYPE, +} + +LE_LEVEL_REACHED: Dict[LESecurityLevel, Callable[[BumbleConnection], bool]] = { + LE_LEVEL1: lambda connection: True, + LE_LEVEL2: lambda connection: connection.encryption != 0, + LE_LEVEL3: lambda connection: connection.encryption != 0 + and connection.authenticated, + LE_LEVEL4: lambda connection: connection.encryption != 0 + and connection.authenticated + and connection.sc, +} + + +class SecurityService(SecurityServicer): + def __init__(self, device: Device, config: Config) -> None: + self.log = utils.BumbleServerLoggerAdapter( + logging.getLogger(), {'service_name': 'Security', 'device': device} + ) + self.event_queue: Optional[asyncio.Queue[PairingEvent]] = None + self.event_answer: Optional[AsyncIterator[PairingEventAnswer]] = None + self.device = device + self.config = config + + def pairing_config_factory(connection: BumbleConnection) -> PairingConfig: + return PairingConfig( + sc=config.pairing_sc_enable, + mitm=config.pairing_mitm_enable, + bonding=config.pairing_bonding_enable, + delegate=PairingDelegate( + connection, + self, + io_capability=config.io_capability, + local_initiator_key_distribution=config.smp_local_initiator_key_distribution, + local_responder_key_distribution=config.smp_local_responder_key_distribution, + ), + ) + + self.device.pairing_config_factory = pairing_config_factory + + @utils.rpc + async def OnPairing( + self, request: AsyncIterator[PairingEventAnswer], context: grpc.ServicerContext + ) -> AsyncGenerator[PairingEvent, None]: + self.log.info('OnPairing') + + if self.event_queue is not None: + raise RuntimeError('already streaming pairing events') + + if len(self.device.connections): + raise RuntimeError( + 'the `OnPairing` method shall be initiated before establishing any connections.' + ) + + self.event_queue = asyncio.Queue() + self.event_answer = request + + try: + while event := await self.event_queue.get(): + yield event + + finally: + self.event_queue = None + self.event_answer = None + + @utils.rpc + async def Secure( + self, request: SecureRequest, context: grpc.ServicerContext + ) -> SecureResponse: + connection_handle = int.from_bytes(request.connection.cookie.value, 'big') + self.log.info(f"Secure: {connection_handle}") + + connection = self.device.lookup_connection(connection_handle) + assert connection + + oneof = request.WhichOneof('level') + level = getattr(request, oneof) + assert {BT_BR_EDR_TRANSPORT: 'classic', BT_LE_TRANSPORT: 'le'}[ + connection.transport + ] == oneof + + # security level already reached + if self.reached_security_level(connection, level): + return SecureResponse(success=empty_pb2.Empty()) + + # trigger pairing if needed + if self.need_pairing(connection, level): + try: + self.log.info('Pair...') + + if ( + connection.transport == BT_LE_TRANSPORT + and connection.role == BT_PERIPHERAL_ROLE + ): + wait_for_security: asyncio.Future[ + bool + ] = asyncio.get_running_loop().create_future() + connection.on("pairing", lambda *_: wait_for_security.set_result(True)) # type: ignore + connection.on("pairing_failure", wait_for_security.set_exception) + + connection.request_pairing() + + await wait_for_security + else: + await connection.pair() + + self.log.info('Paired') + except asyncio.CancelledError: + self.log.warning("Connection died during encryption") + return SecureResponse(connection_died=empty_pb2.Empty()) + except (HCI_Error, ProtocolError) as e: + self.log.warning(f"Pairing failure: {e}") + return SecureResponse(pairing_failure=empty_pb2.Empty()) + + # trigger authentication if needed + if self.need_authentication(connection, level): + try: + self.log.info('Authenticate...') + await connection.authenticate() + self.log.info('Authenticated') + except asyncio.CancelledError: + self.log.warning("Connection died during authentication") + return SecureResponse(connection_died=empty_pb2.Empty()) + except (HCI_Error, ProtocolError) as e: + self.log.warning(f"Authentication failure: {e}") + return SecureResponse(authentication_failure=empty_pb2.Empty()) + + # trigger encryption if needed + if self.need_encryption(connection, level): + try: + self.log.info('Encrypt...') + await connection.encrypt() + self.log.info('Encrypted') + except asyncio.CancelledError: + self.log.warning("Connection died during encryption") + return SecureResponse(connection_died=empty_pb2.Empty()) + except (HCI_Error, ProtocolError) as e: + self.log.warning(f"Encryption failure: {e}") + return SecureResponse(encryption_failure=empty_pb2.Empty()) + + # security level has been reached ? + if self.reached_security_level(connection, level): + return SecureResponse(success=empty_pb2.Empty()) + return SecureResponse(not_reached=empty_pb2.Empty()) + + @utils.rpc + async def WaitSecurity( + self, request: WaitSecurityRequest, context: grpc.ServicerContext + ) -> WaitSecurityResponse: + connection_handle = int.from_bytes(request.connection.cookie.value, 'big') + self.log.info(f"WaitSecurity: {connection_handle}") + + connection = self.device.lookup_connection(connection_handle) + assert connection + + assert request.level + level = request.level + assert {BT_BR_EDR_TRANSPORT: 'classic', BT_LE_TRANSPORT: 'le'}[ + connection.transport + ] == request.level_variant() + + wait_for_security: asyncio.Future[ + str + ] = asyncio.get_running_loop().create_future() + authenticate_task: Optional[asyncio.Future[None]] = None + + async def authenticate() -> None: + assert connection + if (encryption := connection.encryption) != 0: + self.log.debug('Disable encryption...') + try: + await connection.encrypt(enable=False) + except: + pass + self.log.debug('Disable encryption: done') + + self.log.debug('Authenticate...') + await connection.authenticate() + self.log.debug('Authenticate: done') + + if encryption != 0 and connection.encryption != encryption: + self.log.debug('Re-enable encryption...') + await connection.encrypt() + self.log.debug('Re-enable encryption: done') + + def set_failure(name: str) -> Callable[..., None]: + def wrapper(*args: Any) -> None: + self.log.info(f'Wait for security: error `{name}`: {args}') + wait_for_security.set_result(name) + + return wrapper + + def try_set_success(*_: Any) -> None: + assert connection + if self.reached_security_level(connection, level): + self.log.info('Wait for security: done') + wait_for_security.set_result('success') + + def on_encryption_change(*_: Any) -> None: + assert connection + if self.reached_security_level(connection, level): + self.log.info('Wait for security: done') + wait_for_security.set_result('success') + elif ( + connection.transport == BT_BR_EDR_TRANSPORT + and self.need_authentication(connection, level) + ): + nonlocal authenticate_task + if authenticate_task is None: + authenticate_task = asyncio.create_task(authenticate()) + + listeners: Dict[str, Callable[..., None]] = { + 'disconnection': set_failure('connection_died'), + 'pairing_failure': set_failure('pairing_failure'), + 'connection_authentication_failure': set_failure('authentication_failure'), + 'connection_encryption_failure': set_failure('encryption_failure'), + 'pairing': try_set_success, + 'connection_authentication': try_set_success, + 'connection_encryption_change': on_encryption_change, + } + + # register event handlers + for event, listener in listeners.items(): + connection.on(event, listener) + + # security level already reached + if self.reached_security_level(connection, level): + return WaitSecurityResponse(success=empty_pb2.Empty()) + + self.log.info('Wait for security...') + kwargs = {} + kwargs[await wait_for_security] = empty_pb2.Empty() + + # remove event handlers + for event, listener in listeners.items(): + connection.remove_listener(event, listener) # type: ignore + + # wait for `authenticate` to finish if any + if authenticate_task is not None: + self.log.info('Wait for authentication...') + try: + await authenticate_task # type: ignore + except: + pass + self.log.info('Authenticated') + + return WaitSecurityResponse(**kwargs) + + def reached_security_level( + self, connection: BumbleConnection, level: Union[SecurityLevel, LESecurityLevel] + ) -> bool: + self.log.debug( + str( + { + 'level': level, + 'encryption': connection.encryption, + 'authenticated': connection.authenticated, + 'sc': connection.sc, + 'link_key_type': connection.link_key_type, + } + ) + ) + + if isinstance(level, LESecurityLevel): + return LE_LEVEL_REACHED[level](connection) + + return BR_LEVEL_REACHED[level](connection) + + def need_pairing(self, connection: BumbleConnection, level: int) -> bool: + if connection.transport == BT_LE_TRANSPORT: + return level >= LE_LEVEL3 and not connection.authenticated + return False + + def need_authentication(self, connection: BumbleConnection, level: int) -> bool: + if connection.transport == BT_LE_TRANSPORT: + return False + if level == LEVEL2 and connection.encryption != 0: + return not connection.authenticated + return level >= LEVEL2 and not connection.authenticated + + def need_encryption(self, connection: BumbleConnection, level: int) -> bool: + # TODO(abel): need to support MITM + if connection.transport == BT_LE_TRANSPORT: + return level == LE_LEVEL2 and not connection.encryption + return level >= LEVEL2 and not connection.encryption + + +class SecurityStorageService(SecurityStorageServicer): + def __init__(self, device: Device, config: Config) -> None: + self.log = utils.BumbleServerLoggerAdapter( + logging.getLogger(), {'service_name': 'SecurityStorage', 'device': device} + ) + self.device = device + self.config = config + + @utils.rpc + async def IsBonded( + self, request: IsBondedRequest, context: grpc.ServicerContext + ) -> wrappers_pb2.BoolValue: + address = utils.address_from_request(request, request.WhichOneof("address")) + self.log.info(f"IsBonded: {address}") + + if self.device.keystore is not None: + is_bonded = await self.device.keystore.get(str(address)) is not None + else: + is_bonded = False + + return BoolValue(value=is_bonded) + + @utils.rpc + async def DeleteBond( + self, request: DeleteBondRequest, context: grpc.ServicerContext + ) -> empty_pb2.Empty: + address = utils.address_from_request(request, request.WhichOneof("address")) + self.log.info(f"DeleteBond: {address}") + + if self.device.keystore is not None: + with suppress(KeyError): + await self.device.keystore.delete(str(address)) + + return empty_pb2.Empty() diff --git a/bumble/pandora/utils.py b/bumble/pandora/utils.py new file mode 100644 index 0000000..c07a5bc --- /dev/null +++ b/bumble/pandora/utils.py @@ -0,0 +1,112 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import contextlib +import functools +import grpc +import inspect +import logging + +from bumble.device import Device +from bumble.hci import Address +from google.protobuf.message import Message # pytype: disable=pyi-error +from typing import Any, Dict, Generator, MutableMapping, Optional, Tuple + +ADDRESS_TYPES: Dict[str, int] = { + "public": Address.PUBLIC_DEVICE_ADDRESS, + "random": Address.RANDOM_DEVICE_ADDRESS, + "public_identity": Address.PUBLIC_IDENTITY_ADDRESS, + "random_static_identity": Address.RANDOM_IDENTITY_ADDRESS, +} + + +def address_from_request(request: Message, field: Optional[str]) -> Address: + if field is None: + return Address.ANY + return Address(bytes(reversed(getattr(request, field))), ADDRESS_TYPES[field]) + + +class BumbleServerLoggerAdapter(logging.LoggerAdapter): # type: ignore + """Formats logs from the PandoraClient.""" + + def process( + self, msg: str, kwargs: MutableMapping[str, Any] + ) -> Tuple[str, MutableMapping[str, Any]]: + assert self.extra + service_name = self.extra['service_name'] + assert isinstance(service_name, str) + device = self.extra['device'] + assert isinstance(device, Device) + addr_bytes = bytes( + reversed(bytes(device.public_address)) + ) # pytype: disable=attribute-error + addr = ':'.join([f'{x:02X}' for x in addr_bytes[4:]]) + return (f'[bumble.{service_name}:{addr}] {msg}', kwargs) + + +@contextlib.contextmanager +def exception_to_rpc_error( + context: grpc.ServicerContext, +) -> Generator[None, None, None]: + try: + yield None + except NotImplementedError as e: + context.set_code(grpc.StatusCode.UNIMPLEMENTED) # type: ignore + context.set_details(str(e)) # type: ignore + except ValueError as e: + context.set_code(grpc.StatusCode.INVALID_ARGUMENT) # type: ignore + context.set_details(str(e)) # type: ignore + except RuntimeError as e: + context.set_code(grpc.StatusCode.ABORTED) # type: ignore + context.set_details(str(e)) # type: ignore + + +# Decorate an RPC servicer method with a wrapper that transform exceptions to gRPC errors. +def rpc(func: Any) -> Any: + @functools.wraps(func) + async def asyncgen_wrapper( + self: Any, request: Any, context: grpc.ServicerContext + ) -> Any: + with exception_to_rpc_error(context): + async for v in func(self, request, context): + yield v + + @functools.wraps(func) + async def async_wrapper( + self: Any, request: Any, context: grpc.ServicerContext + ) -> Any: + with exception_to_rpc_error(context): + return await func(self, request, context) + + @functools.wraps(func) + def gen_wrapper(self: Any, request: Any, context: grpc.ServicerContext) -> Any: + with exception_to_rpc_error(context): + for v in func(self, request, context): + yield v + + @functools.wraps(func) + def wrapper(self: Any, request: Any, context: grpc.ServicerContext) -> Any: + with exception_to_rpc_error(context): + return func(self, request, context) + + if inspect.isasyncgenfunction(func): + return asyncgen_wrapper + + if inspect.iscoroutinefunction(func): + return async_wrapper + + if inspect.isgenerator(func): + return gen_wrapper + + return wrapper diff --git a/bumble/profiles/asha_service.py b/bumble/profiles/asha_service.py new file mode 100644 index 0000000..6898397 --- /dev/null +++ b/bumble/profiles/asha_service.py @@ -0,0 +1,188 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import struct +import logging +from typing import List +from ..core import AdvertisingData +from ..device import Device, Connection +from ..gatt import ( + GATT_ASHA_SERVICE, + GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC, + GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC, + GATT_ASHA_AUDIO_STATUS_CHARACTERISTIC, + GATT_ASHA_VOLUME_CHARACTERISTIC, + GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC, + TemplateService, + Characteristic, + CharacteristicValue, +) +from ..utils import AsyncRunner + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- +logger = logging.getLogger(__name__) + + +# ----------------------------------------------------------------------------- +class AshaService(TemplateService): + UUID = GATT_ASHA_SERVICE + OPCODE_START = 1 + OPCODE_STOP = 2 + OPCODE_STATUS = 3 + PROTOCOL_VERSION = 0x01 + RESERVED_FOR_FUTURE_USE = [00, 00] + FEATURE_MAP = [0x01] # [LE CoC audio output streaming supported] + SUPPORTED_CODEC_ID = [0x02, 0x01] # Codec IDs [G.722 at 16 kHz] + RENDER_DELAY = [00, 00] + + def __init__(self, capability: int, hisyncid: List[int], device: Device, psm=0): + self.hisyncid = hisyncid + self.capability = capability # Device Capabilities [Left, Monaural] + self.device = device + self.audio_out_data = b'' + self.psm = psm # a non-zero psm is mainly for testing purpose + + # Handler for volume control + def on_volume_write(connection, value): + logger.info(f'--- VOLUME Write:{value[0]}') + self.emit('volume', connection, value[0]) + + # Handler for audio control commands + def on_audio_control_point_write(connection: Connection, value): + logger.info(f'--- AUDIO CONTROL POINT Write:{value.hex()}') + opcode = value[0] + if opcode == AshaService.OPCODE_START: + # Start + audio_type = ('Unknown', 'Ringtone', 'Phone Call', 'Media')[value[2]] + logger.info( + f'### START: codec={value[1]}, ' + f'audio_type={audio_type}, ' + f'volume={value[3]}, ' + f'otherstate={value[4]}' + ) + self.emit( + 'start', + connection, + { + 'codec': value[1], + 'audiotype': value[2], + 'volume': value[3], + 'otherstate': value[4], + }, + ) + elif opcode == AshaService.OPCODE_STOP: + logger.info('### STOP') + self.emit('stop', connection) + elif opcode == AshaService.OPCODE_STATUS: + logger.info(f'### STATUS: connected={value[1]}') + + # OPCODE_STATUS does not need audio status point update + if opcode != AshaService.OPCODE_STATUS: + AsyncRunner.spawn( + device.notify_subscribers( + self.audio_status_characteristic, force=True + ) + ) + + self.read_only_properties_characteristic = Characteristic( + GATT_ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + bytes( + [ + AshaService.PROTOCOL_VERSION, # Version + self.capability, + ] + ) + + bytes(self.hisyncid) + + bytes(AshaService.FEATURE_MAP) + + bytes(AshaService.RENDER_DELAY) + + bytes(AshaService.RESERVED_FOR_FUTURE_USE) + + bytes(AshaService.SUPPORTED_CODEC_ID), + ) + + self.audio_control_point_characteristic = Characteristic( + GATT_ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC, + Characteristic.Properties.WRITE + | Characteristic.Properties.WRITE_WITHOUT_RESPONSE, + Characteristic.WRITEABLE, + CharacteristicValue(write=on_audio_control_point_write), + ) + self.audio_status_characteristic = Characteristic( + GATT_ASHA_AUDIO_STATUS_CHARACTERISTIC, + Characteristic.Properties.READ | Characteristic.Properties.NOTIFY, + Characteristic.READABLE, + bytes([0]), + ) + self.volume_characteristic = Characteristic( + GATT_ASHA_VOLUME_CHARACTERISTIC, + Characteristic.Properties.WRITE_WITHOUT_RESPONSE, + Characteristic.WRITEABLE, + CharacteristicValue(write=on_volume_write), + ) + + # Register an L2CAP CoC server + def on_coc(channel): + def on_data(data): + logging.debug(f'<<< data received:{data}') + + self.emit('data', channel.connection, data) + self.audio_out_data += data + + channel.sink = on_data + + # let the server find a free PSM + self.psm = self.device.register_l2cap_channel_server(self.psm, on_coc, 8) + self.le_psm_out_characteristic = Characteristic( + GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + struct.pack('<H', self.psm), + ) + + characteristics = [ + self.read_only_properties_characteristic, + self.audio_control_point_characteristic, + self.audio_status_characteristic, + self.volume_characteristic, + self.le_psm_out_characteristic, + ] + + super().__init__(characteristics) + + def get_advertising_data(self): + # Advertisement only uses 4 least significant bytes of the HiSyncId. + return bytes( + AdvertisingData( + [ + ( + AdvertisingData.SERVICE_DATA_16_BIT_UUID, + bytes(GATT_ASHA_SERVICE) + + bytes( + [ + AshaService.PROTOCOL_VERSION, + self.capability, + ] + ) + + bytes(self.hisyncid[:4]), + ), + ] + ) + ) diff --git a/bumble/profiles/battery_service.py b/bumble/profiles/battery_service.py index a978c05..211fee0 100644 --- a/bumble/profiles/battery_service.py +++ b/bumble/profiles/battery_service.py @@ -23,7 +23,7 @@ from ..gatt import ( TemplateService, Characteristic, CharacteristicValue, - PackedCharacteristicAdapter + PackedCharacteristicAdapter, ) @@ -36,11 +36,11 @@ class BatteryService(TemplateService): self.battery_level_characteristic = PackedCharacteristicAdapter( Characteristic( GATT_BATTERY_LEVEL_CHARACTERISTIC, - Characteristic.READ | Characteristic.NOTIFY, + Characteristic.Properties.READ | Characteristic.Properties.NOTIFY, Characteristic.READABLE, - CharacteristicValue(read=read_battery_level) + CharacteristicValue(read=read_battery_level), ), - format=BatteryService.BATTERY_LEVEL_FORMAT + pack_format=BatteryService.BATTERY_LEVEL_FORMAT, ) super().__init__([self.battery_level_characteristic]) @@ -52,10 +52,11 @@ class BatteryServiceProxy(ProfileServiceProxy): def __init__(self, service_proxy): self.service_proxy = service_proxy - if characteristics := service_proxy.get_characteristics_by_uuid(GATT_BATTERY_LEVEL_CHARACTERISTIC): + if characteristics := service_proxy.get_characteristics_by_uuid( + GATT_BATTERY_LEVEL_CHARACTERISTIC + ): self.battery_level = PackedCharacteristicAdapter( - characteristics[0], - format=BatteryService.BATTERY_LEVEL_FORMAT + characteristics[0], pack_format=BatteryService.BATTERY_LEVEL_FORMAT ) else: self.battery_level = None diff --git a/bumble/profiles/device_information_service.py b/bumble/profiles/device_information_service.py index 99765b4..09bfd6c 100644 --- a/bumble/profiles/device_information_service.py +++ b/bumble/profiles/device_information_service.py @@ -17,7 +17,7 @@ # Imports # ----------------------------------------------------------------------------- import struct -from typing import Tuple +from typing import Optional, Tuple from ..gatt_client import ProfileServiceProxy from ..gatt import ( @@ -33,7 +33,7 @@ from ..gatt import ( TemplateService, Characteristic, DelegatedCharacteristicAdapter, - UTF8CharacteristicAdapter + UTF8CharacteristicAdapter, ) @@ -52,49 +52,50 @@ class DeviceInformationService(TemplateService): def __init__( self, - manufacturer_name: str = None, - model_number: str = None, - serial_number: str = None, - hardware_revision: str = None, - firmware_revision: str = None, - software_revision: str = None, - system_id: Tuple[int, int] = None, # (OUI, Manufacturer ID) - ieee_regulatory_certification_data_list: bytes = None + manufacturer_name: Optional[str] = None, + model_number: Optional[str] = None, + serial_number: Optional[str] = None, + hardware_revision: Optional[str] = None, + firmware_revision: Optional[str] = None, + software_revision: Optional[str] = None, + system_id: Optional[Tuple[int, int]] = None, # (OUI, Manufacturer ID) + ieee_regulatory_certification_data_list: Optional[bytes] = None # TODO: pnp_id ): characteristics = [ Characteristic( - uuid, - Characteristic.READ, - Characteristic.READABLE, - field + uuid, Characteristic.Properties.READ, Characteristic.READABLE, field ) for (field, uuid) in ( (manufacturer_name, GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC), - (model_number, GATT_MODEL_NUMBER_STRING_CHARACTERISTIC), - (serial_number, GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC), + (model_number, GATT_MODEL_NUMBER_STRING_CHARACTERISTIC), + (serial_number, GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC), (hardware_revision, GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC), (firmware_revision, GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC), - (software_revision, GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC) + (software_revision, GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC), ) if field is not None ] if system_id is not None: - characteristics.append(Characteristic( - GATT_SYSTEM_ID_CHARACTERISTIC, - Characteristic.READ, - Characteristic.READABLE, - self.pack_system_id(*system_id) - )) + characteristics.append( + Characteristic( + GATT_SYSTEM_ID_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + self.pack_system_id(*system_id), + ) + ) if ieee_regulatory_certification_data_list is not None: - characteristics.append(Characteristic( - GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC, - Characteristic.READ, - Characteristic.READABLE, - ieee_regulatory_certification_data_list - )) + characteristics.append( + Characteristic( + GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + ieee_regulatory_certification_data_list, + ) + ) super().__init__(characteristics) @@ -108,11 +109,11 @@ class DeviceInformationServiceProxy(ProfileServiceProxy): for (field, uuid) in ( ('manufacturer_name', GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC), - ('model_number', GATT_MODEL_NUMBER_STRING_CHARACTERISTIC), - ('serial_number', GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC), + ('model_number', GATT_MODEL_NUMBER_STRING_CHARACTERISTIC), + ('serial_number', GATT_SERIAL_NUMBER_STRING_CHARACTERISTIC), ('hardware_revision', GATT_HARDWARE_REVISION_STRING_CHARACTERISTIC), ('firmware_revision', GATT_FIRMWARE_REVISION_STRING_CHARACTERISTIC), - ('software_revision', GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC) + ('software_revision', GATT_SOFTWARE_REVISION_STRING_CHARACTERISTIC), ): if characteristics := service_proxy.get_characteristics_by_uuid(uuid): characteristic = UTF8CharacteristicAdapter(characteristics[0]) @@ -120,16 +121,20 @@ class DeviceInformationServiceProxy(ProfileServiceProxy): characteristic = None self.__setattr__(field, characteristic) - if characteristics := service_proxy.get_characteristics_by_uuid(GATT_SYSTEM_ID_CHARACTERISTIC): + if characteristics := service_proxy.get_characteristics_by_uuid( + GATT_SYSTEM_ID_CHARACTERISTIC + ): self.system_id = DelegatedCharacteristicAdapter( characteristics[0], encode=lambda v: DeviceInformationService.pack_system_id(*v), - decode=DeviceInformationService.unpack_system_id + decode=DeviceInformationService.unpack_system_id, ) else: self.system_id = None - if characteristics := service_proxy.get_characteristics_by_uuid(GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC): + if characteristics := service_proxy.get_characteristics_by_uuid( + GATT_REGULATORY_CERTIFICATION_DATA_LIST_CHARACTERISTIC + ): self.ieee_regulatory_certification_data_list = characteristics[0] else: self.ieee_regulatory_certification_data_list = None diff --git a/bumble/profiles/heart_rate_service.py b/bumble/profiles/heart_rate_service.py new file mode 100644 index 0000000..c7d3018 --- /dev/null +++ b/bumble/profiles/heart_rate_service.py @@ -0,0 +1,237 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +from enum import IntEnum +import struct + +from ..gatt_client import ProfileServiceProxy +from ..att import ATT_Error +from ..gatt import ( + GATT_HEART_RATE_SERVICE, + GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC, + GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC, + GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC, + TemplateService, + Characteristic, + CharacteristicValue, + DelegatedCharacteristicAdapter, + PackedCharacteristicAdapter, +) + + +# ----------------------------------------------------------------------------- +class HeartRateService(TemplateService): + UUID = GATT_HEART_RATE_SERVICE + HEART_RATE_CONTROL_POINT_FORMAT = 'B' + CONTROL_POINT_NOT_SUPPORTED = 0x80 + RESET_ENERGY_EXPENDED = 0x01 + + class BodySensorLocation(IntEnum): + OTHER = (0,) + CHEST = (1,) + WRIST = (2,) + FINGER = (3,) + HAND = (4,) + EAR_LOBE = (5,) + FOOT = 6 + + class HeartRateMeasurement: + def __init__( + self, + heart_rate, + sensor_contact_detected=None, + energy_expended=None, + rr_intervals=None, + ): + if heart_rate < 0 or heart_rate > 0xFFFF: + raise ValueError('heart_rate out of range') + + if energy_expended is not None and ( + energy_expended < 0 or energy_expended > 0xFFFF + ): + raise ValueError('energy_expended out of range') + + if rr_intervals: + for rr_interval in rr_intervals: + if rr_interval < 0 or rr_interval * 1024 > 0xFFFF: + raise ValueError('rr_intervals out of range') + + self.heart_rate = heart_rate + self.sensor_contact_detected = sensor_contact_detected + self.energy_expended = energy_expended + self.rr_intervals = rr_intervals + + @classmethod + def from_bytes(cls, data): + flags = data[0] + offset = 1 + + if flags & 1: + hr = struct.unpack_from('<H', data, offset)[0] + offset += 2 + else: + hr = struct.unpack_from('B', data, offset)[0] + offset += 1 + + if flags & (1 << 2): + sensor_contact_detected = flags & (1 << 1) != 0 + else: + sensor_contact_detected = None + + if flags & (1 << 3): + energy_expended = struct.unpack_from('<H', data, offset)[0] + offset += 2 + else: + energy_expended = None + + if flags & (1 << 4): + rr_intervals = tuple( + struct.unpack_from('<H', data, offset + i * 2)[0] / 1024 + for i in range((len(data) - offset) // 2) + ) + else: + rr_intervals = () + + return cls(hr, sensor_contact_detected, energy_expended, rr_intervals) + + def __bytes__(self): + if self.heart_rate < 256: + flags = 0 + data = struct.pack('B', self.heart_rate) + else: + flags = 1 + data = struct.pack('<H', self.heart_rate) + + if self.sensor_contact_detected is not None: + flags |= ((1 if self.sensor_contact_detected else 0) << 1) | (1 << 2) + + if self.energy_expended is not None: + flags |= 1 << 3 + data += struct.pack('<H', self.energy_expended) + + if self.rr_intervals: + flags |= 1 << 4 + data += b''.join( + [ + struct.pack('<H', int(rr_interval * 1024)) + for rr_interval in self.rr_intervals + ] + ) + + return bytes([flags]) + data + + def __str__(self): + return ( + f'HeartRateMeasurement(heart_rate={self.heart_rate},' + f' sensor_contact_detected={self.sensor_contact_detected},' + f' energy_expended={self.energy_expended},' + f' rr_intervals={self.rr_intervals})' + ) + + def __init__( + self, + read_heart_rate_measurement, + body_sensor_location=None, + reset_energy_expended=None, + ): + self.heart_rate_measurement_characteristic = DelegatedCharacteristicAdapter( + Characteristic( + GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC, + Characteristic.Properties.NOTIFY, + 0, + CharacteristicValue(read=read_heart_rate_measurement), + ), + # pylint: disable=unnecessary-lambda + encode=lambda value: bytes(value), + ) + characteristics = [self.heart_rate_measurement_characteristic] + + if body_sensor_location is not None: + self.body_sensor_location_characteristic = Characteristic( + GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + bytes([int(body_sensor_location)]), + ) + characteristics.append(self.body_sensor_location_characteristic) + + if reset_energy_expended: + + def write_heart_rate_control_point_value(connection, value): + if value == self.RESET_ENERGY_EXPENDED: + if reset_energy_expended is not None: + reset_energy_expended(connection) + else: + raise ATT_Error(self.CONTROL_POINT_NOT_SUPPORTED) + + self.heart_rate_control_point_characteristic = PackedCharacteristicAdapter( + Characteristic( + GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC, + Characteristic.Properties.WRITE, + Characteristic.WRITEABLE, + CharacteristicValue(write=write_heart_rate_control_point_value), + ), + pack_format=HeartRateService.HEART_RATE_CONTROL_POINT_FORMAT, + ) + characteristics.append(self.heart_rate_control_point_characteristic) + + super().__init__(characteristics) + + +# ----------------------------------------------------------------------------- +class HeartRateServiceProxy(ProfileServiceProxy): + SERVICE_CLASS = HeartRateService + + def __init__(self, service_proxy): + self.service_proxy = service_proxy + + if characteristics := service_proxy.get_characteristics_by_uuid( + GATT_HEART_RATE_MEASUREMENT_CHARACTERISTIC + ): + self.heart_rate_measurement = DelegatedCharacteristicAdapter( + characteristics[0], + decode=HeartRateService.HeartRateMeasurement.from_bytes, + ) + else: + self.heart_rate_measurement = None + + if characteristics := service_proxy.get_characteristics_by_uuid( + GATT_BODY_SENSOR_LOCATION_CHARACTERISTIC + ): + self.body_sensor_location = DelegatedCharacteristicAdapter( + characteristics[0], + decode=lambda value: HeartRateService.BodySensorLocation(value[0]), + ) + else: + self.body_sensor_location = None + + if characteristics := service_proxy.get_characteristics_by_uuid( + GATT_HEART_RATE_CONTROL_POINT_CHARACTERISTIC + ): + self.heart_rate_control_point = PackedCharacteristicAdapter( + characteristics[0], + pack_format=HeartRateService.HEART_RATE_CONTROL_POINT_FORMAT, + ) + else: + self.heart_rate_control_point = None + + async def reset_energy_expended(self): + if self.heart_rate_control_point is not None: + return await self.heart_rate_control_point.write_value( + HeartRateService.RESET_ENERGY_EXPENDED + ) diff --git a/bumble/profiles/py.typed b/bumble/profiles/py.typed new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bumble/profiles/py.typed diff --git a/bumble/py.typed b/bumble/py.typed new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bumble/py.typed diff --git a/bumble/rfcomm.py b/bumble/rfcomm.py index 527eaf1..71be8dc 100644 --- a/bumble/rfcomm.py +++ b/bumble/rfcomm.py @@ -17,10 +17,12 @@ # ----------------------------------------------------------------------------- import logging import asyncio -from colors import color -from .utils import EventEmitter -from .core import InvalidStateError, ProtocolError, ConnectionError +from pyee import EventEmitter + +from . import core +from .colors import color +from .core import BT_BR_EDR_TRANSPORT, InvalidStateError, ProtocolError # ----------------------------------------------------------------------------- # Logging @@ -31,6 +33,8 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off + RFCOMM_PSM = 0x0003 @@ -97,22 +101,24 @@ RFCOMM_DEFAULT_PREFERRED_MTU = 1280 RFCOMM_DYNAMIC_CHANNEL_NUMBER_START = 1 RFCOMM_DYNAMIC_CHANNEL_NUMBER_END = 30 +# fmt: on + # ----------------------------------------------------------------------------- -def fcs(buffer): - fcs = 0xFF +def compute_fcs(buffer): + result = 0xFF for byte in buffer: - fcs = CRC_TABLE[fcs ^ byte] - return 0xFF - fcs + result = CRC_TABLE[result ^ byte] + return 0xFF - result # ----------------------------------------------------------------------------- class RFCOMM_Frame: - def __init__(self, type, c_r, dlci, p_f, information = b'', with_credits = False): - self.type = type - self.c_r = c_r - self.dlci = dlci - self.p_f = p_f + def __init__(self, frame_type, c_r, dlci, p_f, information=b'', with_credits=False): + self.type = frame_type + self.c_r = c_r + self.dlci = dlci + self.p_f = p_f self.information = information length = len(information) if with_credits: @@ -123,19 +129,19 @@ class RFCOMM_Frame: else: # 1-byte length indicator self.length = bytes([(length << 1) | 1]) - self.address = (dlci << 2) | (c_r << 1) | 1 - self.control = type | (p_f << 4) - if type == RFCOMM_UIH_FRAME: - self.fcs = fcs(bytes([self.address, self.control])) + self.address = (dlci << 2) | (c_r << 1) | 1 + self.control = frame_type | (p_f << 4) + if frame_type == RFCOMM_UIH_FRAME: + self.fcs = compute_fcs(bytes([self.address, self.control])) else: - self.fcs = fcs(bytes([self.address, self.control]) + self.length) + self.fcs = compute_fcs(bytes([self.address, self.control]) + self.length) def type_name(self): return RFCOMM_FRAME_TYPE_NAMES[self.type] @staticmethod def parse_mcc(data): - type = data[0] >> 2 + mcc_type = data[0] >> 2 c_r = (data[0] >> 1) & 1 length = data[1] if data[1] & 1: @@ -143,13 +149,16 @@ class RFCOMM_Frame: value = data[2:] else: length = (data[3] << 7) & (length >> 1) - value = data[3:3 + length] + value = data[3 : 3 + length] - return (type, c_r, value) + return (mcc_type, c_r, value) @staticmethod - def make_mcc(type, c_r, data): - return bytes([(type << 2 | c_r << 1 | 1) & 0xFF, (len(data) & 0x7F) << 1 | 1]) + data + def make_mcc(mcc_type, c_r, data): + return ( + bytes([(mcc_type << 2 | c_r << 1 | 1) & 0xFF, (len(data) & 0x7F) << 1 | 1]) + + data + ) @staticmethod def sabm(c_r, dlci): @@ -168,15 +177,17 @@ class RFCOMM_Frame: return RFCOMM_Frame(RFCOMM_DISC_FRAME, c_r, dlci, 1) @staticmethod - def uih(c_r, dlci, information, p_f = 0): - return RFCOMM_Frame(RFCOMM_UIH_FRAME, c_r, dlci, p_f, information, with_credits = (p_f == 1)) + def uih(c_r, dlci, information, p_f=0): + return RFCOMM_Frame( + RFCOMM_UIH_FRAME, c_r, dlci, p_f, information, with_credits=(p_f == 1) + ) @staticmethod def from_bytes(data): # Extract fields dlci = (data[0] >> 2) & 0x3F c_r = (data[0] >> 1) & 0x01 - type = data[1] & 0xEF + frame_type = data[1] & 0xEF p_f = (data[1] >> 4) & 0x01 length = data[2] if length & 0x01: @@ -188,132 +199,182 @@ class RFCOMM_Frame: fcs = data[-1] # Construct the frame and check the CRC - frame = RFCOMM_Frame(type, c_r, dlci, p_f, information) + frame = RFCOMM_Frame(frame_type, c_r, dlci, p_f, information) if frame.fcs != fcs: - logger.warn(f'FCS mismatch: got {fcs:02X}, expected {frame.fcs:02X}') + logger.warning(f'FCS mismatch: got {fcs:02X}, expected {frame.fcs:02X}') raise ValueError('fcs mismatch') return frame def __bytes__(self): - return bytes([self.address, self.control]) + self.length + self.information + bytes([self.fcs]) + return ( + bytes([self.address, self.control]) + + self.length + + self.information + + bytes([self.fcs]) + ) def __str__(self): - return f'{color(self.type_name(), "yellow")}(c/r={self.c_r},dlci={self.dlci},p/f={self.p_f},length={len(self.information)},fcs=0x{self.fcs:02X})' + return ( + f'{color(self.type_name(), "yellow")}' + f'(c/r={self.c_r},' + f'dlci={self.dlci},' + f'p/f={self.p_f},' + f'length={len(self.information)},' + f'fcs=0x{self.fcs:02X})' + ) # ----------------------------------------------------------------------------- class RFCOMM_MCC_PN: - def __init__(self, dlci, cl, priority, ack_timer, max_frame_size, max_retransmissions, window_size): - self.dlci = dlci - self.cl = cl - self.priority = priority - self.ack_timer = ack_timer - self.max_frame_size = max_frame_size + def __init__( + self, + dlci, + cl, + priority, + ack_timer, + max_frame_size, + max_retransmissions, + window_size, + ): + self.dlci = dlci + self.cl = cl + self.priority = priority + self.ack_timer = ack_timer + self.max_frame_size = max_frame_size self.max_retransmissions = max_retransmissions - self.window_size = window_size + self.window_size = window_size @staticmethod def from_bytes(data): return RFCOMM_MCC_PN( - dlci = data[0], - cl = data[1], - priority = data[2], - ack_timer = data[3], - max_frame_size = data[4] | data[5] << 8, - max_retransmissions = data[6], - window_size = data[7] + dlci=data[0], + cl=data[1], + priority=data[2], + ack_timer=data[3], + max_frame_size=data[4] | data[5] << 8, + max_retransmissions=data[6], + window_size=data[7], ) def __bytes__(self): - return bytes([ - self.dlci & 0xFF, - self.cl & 0xFF, - self.priority & 0xFF, - self.ack_timer & 0xFF, - self.max_frame_size & 0xFF, - (self.max_frame_size >> 8) & 0xFF, - self.max_retransmissions & 0xFF, - self.window_size & 0xFF - ]) + return bytes( + [ + self.dlci & 0xFF, + self.cl & 0xFF, + self.priority & 0xFF, + self.ack_timer & 0xFF, + self.max_frame_size & 0xFF, + (self.max_frame_size >> 8) & 0xFF, + self.max_retransmissions & 0xFF, + self.window_size & 0xFF, + ] + ) def __str__(self): - return f'PN(dlci={self.dlci},cl={self.cl},priority={self.priority},ack_timer={self.ack_timer},max_frame_size={self.max_frame_size},max_retransmissions={self.max_retransmissions},window_size={self.window_size})' + return ( + f'PN(dlci={self.dlci},' + f'cl={self.cl},' + f'priority={self.priority},' + f'ack_timer={self.ack_timer},' + f'max_frame_size={self.max_frame_size},' + f'max_retransmissions={self.max_retransmissions},' + f'window_size={self.window_size})' + ) # ----------------------------------------------------------------------------- class RFCOMM_MCC_MSC: def __init__(self, dlci, fc, rtc, rtr, ic, dv): self.dlci = dlci - self.fc = fc - self.rtc = rtc - self.rtr = rtr - self.ic = ic - self.dv = dv + self.fc = fc + self.rtc = rtc + self.rtr = rtr + self.ic = ic + self.dv = dv @staticmethod def from_bytes(data): return RFCOMM_MCC_MSC( - dlci = data[0] >> 2, - fc = data[1] >> 1 & 1, - rtc = data[1] >> 2 & 1, - rtr = data[1] >> 3 & 1, - ic = data[1] >> 6 & 1, - dv = data[1] >> 7 & 1 + dlci=data[0] >> 2, + fc=data[1] >> 1 & 1, + rtc=data[1] >> 2 & 1, + rtr=data[1] >> 3 & 1, + ic=data[1] >> 6 & 1, + dv=data[1] >> 7 & 1, ) def __bytes__(self): - return bytes([ - (self.dlci << 2) | 3, - 1 | self.fc << 1 | self.rtc << 2 | self.rtr << 3 | self.ic << 6 | self.dv << 7 - ]) + return bytes( + [ + (self.dlci << 2) | 3, + 1 + | self.fc << 1 + | self.rtc << 2 + | self.rtr << 3 + | self.ic << 6 + | self.dv << 7, + ] + ) def __str__(self): - return f'MSC(dlci={self.dlci},fc={self.fc},rtc={self.rtc},rtr={self.rtr},ic={self.ic},dv={self.dv})' + return ( + f'MSC(dlci={self.dlci},' + f'fc={self.fc},' + f'rtc={self.rtc},' + f'rtr={self.rtr},' + f'ic={self.ic},' + f'dv={self.dv})' + ) # ----------------------------------------------------------------------------- class DLC(EventEmitter): # States - INIT = 0x00 - CONNECTING = 0x01 - CONNECTED = 0x02 + INIT = 0x00 + CONNECTING = 0x01 + CONNECTED = 0x02 DISCONNECTING = 0x03 - DISCONNECTED = 0x04 - RESET = 0x05 + DISCONNECTED = 0x04 + RESET = 0x05 STATE_NAMES = { - INIT: 'INIT', - CONNECTING: 'CONNECTING', - CONNECTED: 'CONNECTED', + INIT: 'INIT', + CONNECTING: 'CONNECTING', + CONNECTED: 'CONNECTED', DISCONNECTING: 'DISCONNECTING', - DISCONNECTED: 'DISCONNECTED', - RESET: 'RESET' + DISCONNECTED: 'DISCONNECTED', + RESET: 'RESET', } def __init__(self, multiplexer, dlci, max_frame_size, initial_tx_credits): super().__init__() - self.multiplexer = multiplexer - self.dlci = dlci - self.rx_credits = RFCOMM_DEFAULT_INITIAL_RX_CREDITS + self.multiplexer = multiplexer + self.dlci = dlci + self.rx_credits = RFCOMM_DEFAULT_INITIAL_RX_CREDITS self.rx_threshold = self.rx_credits // 2 - self.tx_credits = initial_tx_credits - self.tx_buffer = b'' - self.state = DLC.INIT - self.role = multiplexer.role - self.c_r = 1 if self.role == Multiplexer.INITIATOR else 0 - self.sink = None + self.tx_credits = initial_tx_credits + self.tx_buffer = b'' + self.state = DLC.INIT + self.role = multiplexer.role + self.c_r = 1 if self.role == Multiplexer.INITIATOR else 0 + self.sink = None + self.connection_result = None # Compute the MTU max_overhead = 4 + 1 # header with 2-byte length + fcs - self.mtu = min(max_frame_size, self.multiplexer.l2cap_channel.mtu - max_overhead) + self.mtu = min( + max_frame_size, self.multiplexer.l2cap_channel.mtu - max_overhead + ) @staticmethod def state_name(state): return DLC.STATE_NAMES[state] def change_state(self, new_state): - logger.debug(f'{self} state change -> {color(self.state_name(new_state), "magenta")}') + logger.debug( + f'{self} state change -> {color(self.state_name(new_state), "magenta")}' + ) self.state = new_state def send_frame(self, frame): @@ -323,58 +384,40 @@ class DLC(EventEmitter): handler = getattr(self, f'on_{frame.type_name()}_frame'.lower()) handler(frame) - def on_sabm_frame(self, frame): + def on_sabm_frame(self, _frame): if self.state != DLC.CONNECTING: - logger.warn(color('!!! received SABM when not in CONNECTING state', 'red')) + logger.warning( + color('!!! received SABM when not in CONNECTING state', 'red') + ) return - self.send_frame(RFCOMM_Frame.ua(c_r = 1 - self.c_r, dlci = self.dlci)) + self.send_frame(RFCOMM_Frame.ua(c_r=1 - self.c_r, dlci=self.dlci)) # Exchange the modem status with the peer - msc = RFCOMM_MCC_MSC( - dlci = self.dlci, - fc = 0, - rtc = 1, - rtr = 1, - ic = 0, - dv = 1 + msc = RFCOMM_MCC_MSC(dlci=self.dlci, fc=0, rtc=1, rtr=1, ic=0, dv=1) + mcc = RFCOMM_Frame.make_mcc( + mcc_type=RFCOMM_MCC_MSC_TYPE, c_r=1, data=bytes(msc) ) - mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_MSC_TYPE, c_r = 1, data = bytes(msc)) logger.debug(f'>>> MCC MSC Command: {msc}') - self.send_frame( - RFCOMM_Frame.uih( - c_r = self.c_r, - dlci = 0, - information = mcc - ) - ) + self.send_frame(RFCOMM_Frame.uih(c_r=self.c_r, dlci=0, information=mcc)) self.change_state(DLC.CONNECTED) self.emit('open') - def on_ua_frame(self, frame): + def on_ua_frame(self, _frame): if self.state != DLC.CONNECTING: - logger.warn(color('!!! received SABM when not in CONNECTING state', 'red')) + logger.warning( + color('!!! received SABM when not in CONNECTING state', 'red') + ) return # Exchange the modem status with the peer - msc = RFCOMM_MCC_MSC( - dlci = self.dlci, - fc = 0, - rtc = 1, - rtr = 1, - ic = 0, - dv = 1 + msc = RFCOMM_MCC_MSC(dlci=self.dlci, fc=0, rtc=1, rtr=1, ic=0, dv=1) + mcc = RFCOMM_Frame.make_mcc( + mcc_type=RFCOMM_MCC_MSC_TYPE, c_r=1, data=bytes(msc) ) - mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_MSC_TYPE, c_r = 1, data = bytes(msc)) logger.debug(f'>>> MCC MSC Command: {msc}') - self.send_frame( - RFCOMM_Frame.uih( - c_r = self.c_r, - dlci = 0, - information = mcc - ) - ) + self.send_frame(RFCOMM_Frame.uih(c_r=self.c_r, dlci=0, information=mcc)) self.change_state(DLC.CONNECTED) self.multiplexer.on_dlc_open_complete(self) @@ -383,29 +426,36 @@ class DLC(EventEmitter): # TODO: handle all states pass - def on_disc_frame(self, frame): + def on_disc_frame(self, _frame): # TODO: handle all states - self.send_frame(RFCOMM_Frame.ua(c_r = 1 - self.c_r, dlci = self.dlci)) + self.send_frame(RFCOMM_Frame.ua(c_r=1 - self.c_r, dlci=self.dlci)) def on_uih_frame(self, frame): data = frame.information if frame.p_f == 1: # With credits - credits = frame.information[0] - self.tx_credits += credits + received_credits = frame.information[0] + self.tx_credits += received_credits - logger.debug(f'<<< Credits [{self.dlci}]: received {credits}, total={self.tx_credits}') + logger.debug( + f'<<< Credits [{self.dlci}]: ' + f'received {received_credits}, total={self.tx_credits}' + ) data = data[1:] - logger.debug(f'{color("<<< Data", "yellow")} [{self.dlci}] {len(data)} bytes, rx_credits={self.rx_credits}: {data.hex()}') + logger.debug( + f'{color("<<< Data", "yellow")} ' + f'[{self.dlci}] {len(data)} bytes, ' + f'rx_credits={self.rx_credits}: {data.hex()}' + ) if len(data) and self.sink: - self.sink(data) + self.sink(data) # pylint: disable=not-callable # Update the credits if self.rx_credits > 0: self.rx_credits -= 1 else: - logger.warn(color('!!! received frame with no rx credits', 'red')) + logger.warning(color('!!! received frame with no rx credits', 'red')) # Check if there's anything to send (including credits) self.process_tx() @@ -417,69 +467,47 @@ class DLC(EventEmitter): if c_r: # Command logger.debug(f'<<< MCC MSC Command: {msc}') - msc = RFCOMM_MCC_MSC( - dlci = self.dlci, - fc = 0, - rtc = 1, - rtr = 1, - ic = 0, - dv = 1 + msc = RFCOMM_MCC_MSC(dlci=self.dlci, fc=0, rtc=1, rtr=1, ic=0, dv=1) + mcc = RFCOMM_Frame.make_mcc( + mcc_type=RFCOMM_MCC_MSC_TYPE, c_r=0, data=bytes(msc) ) - mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_MSC_TYPE, c_r = 0, data = bytes(msc)) logger.debug(f'>>> MCC MSC Response: {msc}') - self.send_frame( - RFCOMM_Frame.uih( - c_r = self.c_r, - dlci = 0, - information = mcc - ) - ) + self.send_frame(RFCOMM_Frame.uih(c_r=self.c_r, dlci=0, information=mcc)) else: # Response logger.debug(f'<<< MCC MSC Response: {msc}') def connect(self): - if not self.state == DLC.INIT: + if self.state != DLC.INIT: raise InvalidStateError('invalid state') self.change_state(DLC.CONNECTING) self.connection_result = asyncio.get_running_loop().create_future() - self.send_frame( - RFCOMM_Frame.sabm( - c_r = self.c_r, - dlci = self.dlci - ) - ) + self.send_frame(RFCOMM_Frame.sabm(c_r=self.c_r, dlci=self.dlci)) def accept(self): - if not self.state == DLC.INIT: + if self.state != DLC.INIT: raise InvalidStateError('invalid state') pn = RFCOMM_MCC_PN( - dlci = self.dlci, - cl = 0xE0, - priority = 7, - ack_timer = 0, - max_frame_size = RFCOMM_DEFAULT_PREFERRED_MTU, - max_retransmissions = 0, - window_size = RFCOMM_DEFAULT_INITIAL_RX_CREDITS + dlci=self.dlci, + cl=0xE0, + priority=7, + ack_timer=0, + max_frame_size=RFCOMM_DEFAULT_PREFERRED_MTU, + max_retransmissions=0, + window_size=RFCOMM_DEFAULT_INITIAL_RX_CREDITS, ) - mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_PN_TYPE, c_r = 0, data = bytes(pn)) + mcc = RFCOMM_Frame.make_mcc(mcc_type=RFCOMM_MCC_PN_TYPE, c_r=0, data=bytes(pn)) logger.debug(f'>>> PN Response: {pn}') - self.send_frame( - RFCOMM_Frame.uih( - c_r = self.c_r, - dlci = 0, - information = mcc - ) - ) + self.send_frame(RFCOMM_Frame.uih(c_r=self.c_r, dlci=0, information=mcc)) self.change_state(DLC.CONNECTING) def rx_credits_needed(self): if self.rx_credits <= self.rx_threshold: return RFCOMM_DEFAULT_INITIAL_RX_CREDITS - self.rx_credits - else: - return 0 + + return 0 def process_tx(self): # Send anything we can (or an empty frame if we need to send rx credits) @@ -487,13 +515,13 @@ class DLC(EventEmitter): while (self.tx_buffer and self.tx_credits > 0) or rx_credits_needed > 0: # Get the next chunk, up to MTU size if rx_credits_needed > 0: - chunk = bytes([rx_credits_needed]) + self.tx_buffer[:self.mtu - 1] - self.tx_buffer = self.tx_buffer[len(chunk) - 1:] + chunk = bytes([rx_credits_needed]) + self.tx_buffer[: self.mtu - 1] + self.tx_buffer = self.tx_buffer[len(chunk) - 1 :] self.rx_credits += rx_credits_needed - tx_credit_spent = (len(chunk) > 1) + tx_credit_spent = len(chunk) > 1 else: - chunk = self.tx_buffer[:self.mtu] - self.tx_buffer = self.tx_buffer[len(chunk):] + chunk = self.tx_buffer[: self.mtu] + self.tx_buffer = self.tx_buffer[len(chunk) :] tx_credit_spent = True # Update the tx credits @@ -502,13 +530,17 @@ class DLC(EventEmitter): self.tx_credits -= 1 # Send the frame - logger.debug(f'>>> sending {len(chunk)} bytes with {rx_credits_needed} credits, rx_credits={self.rx_credits}, tx_credits={self.tx_credits}') + logger.debug( + f'>>> sending {len(chunk)} bytes with {rx_credits_needed} credits, ' + f'rx_credits={self.rx_credits}, ' + f'tx_credits={self.tx_credits}' + ) self.send_frame( RFCOMM_Frame.uih( - c_r = self.c_r, - dlci = self.dlci, - information = chunk, - p_f = 1 if rx_credits_needed > 0 else 0 + c_r=self.c_r, + dlci=self.dlci, + information=chunk, + p_f=1 if rx_credits_needed > 0 else 0, ) ) @@ -517,8 +549,8 @@ class DLC(EventEmitter): # Stream protocol def write(self, data): # We can only send bytes - if type(data) != bytes: - if type(data) == str: + if not isinstance(data, bytes): + if isinstance(data, str): # Automatically convert strings to bytes using UTF-8 data = data.encode('utf-8') else: @@ -542,34 +574,34 @@ class Multiplexer(EventEmitter): RESPONDER = 0x01 # States - INIT = 0x00 - CONNECTING = 0x01 - CONNECTED = 0x02 - OPENING = 0x03 + INIT = 0x00 + CONNECTING = 0x01 + CONNECTED = 0x02 + OPENING = 0x03 DISCONNECTING = 0x04 - DISCONNECTED = 0x05 - RESET = 0x06 + DISCONNECTED = 0x05 + RESET = 0x06 STATE_NAMES = { - INIT: 'INIT', - CONNECTING: 'CONNECTING', - CONNECTED: 'CONNECTED', - OPENING: 'OPENING', + INIT: 'INIT', + CONNECTING: 'CONNECTING', + CONNECTED: 'CONNECTED', + OPENING: 'OPENING', DISCONNECTING: 'DISCONNECTING', - DISCONNECTED: 'DISCONNECTED', - RESET: 'RESET' + DISCONNECTED: 'DISCONNECTED', + RESET: 'RESET', } def __init__(self, l2cap_channel, role): super().__init__() - self.role = role - self.l2cap_channel = l2cap_channel - self.state = Multiplexer.INIT - self.dlcs = {} # DLCs, by DLCI - self.connection_result = None + self.role = role + self.l2cap_channel = l2cap_channel + self.state = Multiplexer.INIT + self.dlcs = {} # DLCs, by DLCI + self.connection_result = None self.disconnection_result = None - self.open_result = None - self.acceptor = None + self.open_result = None + self.acceptor = None # Become a sink for the L2CAP channel l2cap_channel.sink = self.on_pdu @@ -579,7 +611,9 @@ class Multiplexer(EventEmitter): return Multiplexer.STATE_NAMES[state] def change_state(self, new_state): - logger.debug(f'{self} state change -> {color(self.state_name(new_state), "cyan")}') + logger.debug( + f'{self} state change -> {color(self.state_name(new_state), "cyan")}' + ) self.state = new_state def send_frame(self, frame): @@ -595,14 +629,14 @@ class Multiplexer(EventEmitter): self.on_frame(frame) else: if frame.type == RFCOMM_DM_FRAME: - # DM responses are for a DLCI, but since we only create the dlc when we receive - # a PN response (because we need the parameters), we handle DM frames at the Multiplexer - # level + # DM responses are for a DLCI, but since we only create the dlc when we + # receive a PN response (because we need the parameters), we handle DM + # frames at the Multiplexer level self.on_dm_frame(frame) else: dlc = self.dlcs.get(frame.dlci) if dlc is None: - logger.warn(f'no dlc for DLCI {frame.dlci}') + logger.warning(f'no dlc for DLCI {frame.dlci}') return dlc.on_frame(frame) @@ -610,14 +644,14 @@ class Multiplexer(EventEmitter): handler = getattr(self, f'on_{frame.type_name()}_frame'.lower()) handler(frame) - def on_sabm_frame(self, frame): + def on_sabm_frame(self, _frame): if self.state != Multiplexer.INIT: logger.debug('not in INIT state, ignoring SABM') return self.change_state(Multiplexer.CONNECTED) - self.send_frame(RFCOMM_Frame.ua(c_r = 1, dlci = 0)) + self.send_frame(RFCOMM_Frame.ua(c_r=1, dlci=0)) - def on_ua_frame(self, frame): + def on_ua_frame(self, _frame): if self.state == Multiplexer.CONNECTING: self.change_state(Multiplexer.CONNECTED) if self.connection_result: @@ -629,25 +663,34 @@ class Multiplexer(EventEmitter): self.disconnection_result.set_result(None) self.disconnection_result = None - def on_dm_frame(self, frame): + def on_dm_frame(self, _frame): if self.state == Multiplexer.OPENING: self.change_state(Multiplexer.CONNECTED) if self.open_result: - self.open_result.set_exception(ConnectionError(ConnectionError.CONNECTION_REFUSED)) + self.open_result.set_exception( + core.ConnectionError( + core.ConnectionError.CONNECTION_REFUSED, + BT_BR_EDR_TRANSPORT, + self.l2cap_channel.connection.peer_address, + 'rfcomm', + ) + ) else: - logger.warn(f'unexpected state for DM: {self}') + logger.warning(f'unexpected state for DM: {self}') - def on_disc_frame(self, frame): + def on_disc_frame(self, _frame): self.change_state(Multiplexer.DISCONNECTED) - self.send_frame(RFCOMM_Frame.ua(c_r = 0 if self.role == Multiplexer.INITIATOR else 1, dlci = 0)) + self.send_frame( + RFCOMM_Frame.ua(c_r=0 if self.role == Multiplexer.INITIATOR else 1, dlci=0) + ) def on_uih_frame(self, frame): - (type, c_r, value) = RFCOMM_Frame.parse_mcc(frame.information) + (mcc_type, c_r, value) = RFCOMM_Frame.parse_mcc(frame.information) - if type == RFCOMM_MCC_PN_TYPE: + if mcc_type == RFCOMM_MCC_PN_TYPE: pn = RFCOMM_MCC_PN.from_bytes(value) self.on_mcc_pn(c_r, pn) - elif type == RFCOMM_MCC_MSC_TYPE: + elif mcc_type == RFCOMM_MCC_MSC_TYPE: mcs = RFCOMM_MCC_MSC.from_bytes(value) self.on_mcc_msc(c_r, mcs) @@ -663,7 +706,7 @@ class Multiplexer(EventEmitter): if pn.dlci & 1: # Not expected, this is an initiator-side number # TODO: error out - logger.warn(f'invalid DLCI: {pn.dlci}') + logger.warning(f'invalid DLCI: {pn.dlci}') else: if self.acceptor: channel_number = pn.dlci >> 1 @@ -679,10 +722,10 @@ class Multiplexer(EventEmitter): dlc.accept() else: # No acceptor, we're in Disconnected Mode - self.send_frame(RFCOMM_Frame.dm(c_r = 1, dlci = pn.dlci)) + self.send_frame(RFCOMM_Frame.dm(c_r=1, dlci=pn.dlci)) else: # No acceptor?? shouldn't happen - logger.warn(color('!!! no acceptor registered', 'red')) + logger.warning(color('!!! no acceptor registered', 'red')) else: # Response logger.debug(f'>>> PN Response: {pn}') @@ -691,12 +734,12 @@ class Multiplexer(EventEmitter): self.dlcs[pn.dlci] = dlc dlc.connect() else: - logger.warn('ignoring PN response') + logger.warning('ignoring PN response') def on_mcc_msc(self, c_r, msc): dlc = self.dlcs.get(msc.dlci) if dlc is None: - logger.warn(f'no dlc for DLCI {msc.dlci}') + logger.warning(f'no dlc for DLCI {msc.dlci}') return dlc.on_mcc_msc(c_r, msc) @@ -706,7 +749,7 @@ class Multiplexer(EventEmitter): self.change_state(Multiplexer.CONNECTING) self.connection_result = asyncio.get_running_loop().create_future() - self.send_frame(RFCOMM_Frame.sabm(c_r = 1, dlci = 0)) + self.send_frame(RFCOMM_Frame.sabm(c_r=1, dlci=0)) return await self.connection_result async def disconnect(self): @@ -715,34 +758,38 @@ class Multiplexer(EventEmitter): self.disconnection_result = asyncio.get_running_loop().create_future() self.change_state(Multiplexer.DISCONNECTING) - self.send_frame(RFCOMM_Frame.disc(c_r = 1 if self.role == Multiplexer.INITIATOR else 0, dlci = 0)) + self.send_frame( + RFCOMM_Frame.disc( + c_r=1 if self.role == Multiplexer.INITIATOR else 0, dlci=0 + ) + ) await self.disconnection_result async def open_dlc(self, channel): if self.state != Multiplexer.CONNECTED: if self.state == Multiplexer.OPENING: raise InvalidStateError('open already in progress') - else: - raise InvalidStateError('not connected') + + raise InvalidStateError('not connected') pn = RFCOMM_MCC_PN( - dlci = channel << 1, - cl = 0xF0, - priority = 7, - ack_timer = 0, - max_frame_size = RFCOMM_DEFAULT_PREFERRED_MTU, - max_retransmissions = 0, - window_size = RFCOMM_DEFAULT_INITIAL_RX_CREDITS + dlci=channel << 1, + cl=0xF0, + priority=7, + ack_timer=0, + max_frame_size=RFCOMM_DEFAULT_PREFERRED_MTU, + max_retransmissions=0, + window_size=RFCOMM_DEFAULT_INITIAL_RX_CREDITS, ) - mcc = RFCOMM_Frame.make_mcc(type = RFCOMM_MCC_PN_TYPE, c_r = 1, data = bytes(pn)) + mcc = RFCOMM_Frame.make_mcc(mcc_type=RFCOMM_MCC_PN_TYPE, c_r=1, data=bytes(pn)) logger.debug(f'>>> Sending MCC: {pn}') self.open_result = asyncio.get_running_loop().create_future() self.change_state(Multiplexer.OPENING) self.send_frame( RFCOMM_Frame.uih( - c_r = 1 if self.role == Multiplexer.INITIATOR else 0, - dlci = 0, - information = mcc + c_r=1 if self.role == Multiplexer.INITIATOR else 0, + dlci=0, + information=mcc, ) ) result = await self.open_result @@ -762,17 +809,19 @@ class Multiplexer(EventEmitter): # ----------------------------------------------------------------------------- class Client: def __init__(self, device, connection): - self.device = device - self.connection = connection + self.device = device + self.connection = connection self.l2cap_channel = None - self.multiplexer = None + self.multiplexer = None async def start(self): # Create a new L2CAP connection try: - self.l2cap_channel = await self.device.l2cap_channel_manager.connect(self.connection, RFCOMM_PSM) + self.l2cap_channel = await self.device.l2cap_channel_manager.connect( + self.connection, RFCOMM_PSM + ) except ProtocolError as error: - logger.warn(f'L2CAP connection failed: {error}') + logger.warning(f'L2CAP connection failed: {error}') raise # Create a mutliplexer to manage DLCs with the server @@ -796,22 +845,34 @@ class Client: class Server(EventEmitter): def __init__(self, device): super().__init__() - self.device = device + self.device = device self.multiplexer = None - self.acceptors = {} + self.acceptors = {} # Register ourselves with the L2CAP channel manager device.register_l2cap_server(RFCOMM_PSM, self.on_connection) - def listen(self, acceptor): - # Find a free channel number - for channel in range(RFCOMM_DYNAMIC_CHANNEL_NUMBER_START, RFCOMM_DYNAMIC_CHANNEL_NUMBER_END + 1): - if channel not in self.acceptors: - self.acceptors[channel] = acceptor - return channel - - # All channels used... - return 0 + def listen(self, acceptor, channel=0): + if channel: + if channel in self.acceptors: + # Busy + return 0 + else: + # Find a free channel number + for candidate in range( + RFCOMM_DYNAMIC_CHANNEL_NUMBER_START, + RFCOMM_DYNAMIC_CHANNEL_NUMBER_END + 1, + ): + if candidate not in self.acceptors: + channel = candidate + break + + if channel == 0: + # All channels used... + return 0 + + self.acceptors[channel] = acceptor + return channel def on_connection(self, l2cap_channel): logger.debug(f'+++ new L2CAP connection: {l2cap_channel}') diff --git a/bumble/sdp.py b/bumble/sdp.py index 935561e..019b8e6 100644 --- a/bumble/sdp.py +++ b/bumble/sdp.py @@ -15,12 +15,13 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import logging import struct -from colors import color -import colors +from typing import Dict, List, Type from . import core +from .colors import color from .core import InvalidStateError from .hci import HCI_Object, name_or_number, key_with_value @@ -33,6 +34,9 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off +# pylint: disable=line-too-long + SDP_CONTINUATION_WATCHDOG = 64 # Maximum number of continuations we're willing to do SDP_PSM = 0x0001 @@ -112,137 +116,162 @@ SDP_PUBLIC_BROWSE_ROOT = core.UUID.from_16_bits(0x1002, 'PublicBrowseRoot') # To be used in searches where an attribute ID list allows a range to be specified SDP_ALL_ATTRIBUTES_RANGE = (0x0000FFFF, 4) # Express this as tuple so we can convey the desired encoding size +# fmt: on +# pylint: enable=line-too-long +# pylint: disable=invalid-name + # ----------------------------------------------------------------------------- class DataElement: - NIL = 0 + NIL = 0 UNSIGNED_INTEGER = 1 - SIGNED_INTEGER = 2 - UUID = 3 - TEXT_STRING = 4 - BOOLEAN = 5 - SEQUENCE = 6 - ALTERNATIVE = 7 - URL = 8 + SIGNED_INTEGER = 2 + UUID = 3 + TEXT_STRING = 4 + BOOLEAN = 5 + SEQUENCE = 6 + ALTERNATIVE = 7 + URL = 8 TYPE_NAMES = { - NIL: 'NIL', + NIL: 'NIL', UNSIGNED_INTEGER: 'UNSIGNED_INTEGER', - SIGNED_INTEGER: 'SIGNED_INTEGER', - UUID: 'UUID', - TEXT_STRING: 'TEXT_STRING', - BOOLEAN: 'BOOLEAN', - SEQUENCE: 'SEQUENCE', - ALTERNATIVE: 'ALTERNATIVE', - URL: 'URL' + SIGNED_INTEGER: 'SIGNED_INTEGER', + UUID: 'UUID', + TEXT_STRING: 'TEXT_STRING', + BOOLEAN: 'BOOLEAN', + SEQUENCE: 'SEQUENCE', + ALTERNATIVE: 'ALTERNATIVE', + URL: 'URL', } type_constructors = { - NIL: lambda x: DataElement(DataElement.NIL, None), - UNSIGNED_INTEGER: lambda x, y: DataElement(DataElement.UNSIGNED_INTEGER, DataElement.unsigned_integer_from_bytes(x), value_size=y), - SIGNED_INTEGER: lambda x, y: DataElement(DataElement.SIGNED_INTEGER, DataElement.signed_integer_from_bytes(x), value_size=y), - UUID: lambda x: DataElement(DataElement.UUID, core.UUID.from_bytes(bytes(reversed(x)))), - TEXT_STRING: lambda x: DataElement(DataElement.TEXT_STRING, x.decode('utf8')), - BOOLEAN: lambda x: DataElement(DataElement.BOOLEAN, x[0] == 1), - SEQUENCE: lambda x: DataElement(DataElement.SEQUENCE, DataElement.list_from_bytes(x)), - ALTERNATIVE: lambda x: DataElement(DataElement.ALTERNATIVE, DataElement.list_from_bytes(x)), - URL: lambda x: DataElement(DataElement.URL, x.decode('utf8')) + NIL: lambda x: DataElement(DataElement.NIL, None), + UNSIGNED_INTEGER: lambda x, y: DataElement( + DataElement.UNSIGNED_INTEGER, + DataElement.unsigned_integer_from_bytes(x), + value_size=y, + ), + SIGNED_INTEGER: lambda x, y: DataElement( + DataElement.SIGNED_INTEGER, + DataElement.signed_integer_from_bytes(x), + value_size=y, + ), + UUID: lambda x: DataElement( + DataElement.UUID, core.UUID.from_bytes(bytes(reversed(x))) + ), + TEXT_STRING: lambda x: DataElement(DataElement.TEXT_STRING, x.decode('utf8')), + BOOLEAN: lambda x: DataElement(DataElement.BOOLEAN, x[0] == 1), + SEQUENCE: lambda x: DataElement( + DataElement.SEQUENCE, DataElement.list_from_bytes(x) + ), + ALTERNATIVE: lambda x: DataElement( + DataElement.ALTERNATIVE, DataElement.list_from_bytes(x) + ), + URL: lambda x: DataElement(DataElement.URL, x.decode('utf8')), } - def __init__(self, type, value, value_size=None): - self.type = type - self.value = value + def __init__(self, element_type, value, value_size=None): + self.type = element_type + self.value = value self.value_size = value_size - self.bytes = None # Used a cache when parsing from bytes so we can emit a byte-for-byte replica - if type == DataElement.UNSIGNED_INTEGER or type == DataElement.SIGNED_INTEGER: + # Used as a cache when parsing from bytes so we can emit a byte-for-byte replica + self.bytes = None + if element_type in (DataElement.UNSIGNED_INTEGER, DataElement.SIGNED_INTEGER): if value_size is None: raise ValueError('integer types must have a value size specified') @staticmethod - def nil(): + def nil() -> DataElement: return DataElement(DataElement.NIL, None) @staticmethod - def unsigned_integer(value, value_size): + def unsigned_integer(value: int, value_size: int) -> DataElement: return DataElement(DataElement.UNSIGNED_INTEGER, value, value_size) @staticmethod - def unsigned_integer_8(value): + def unsigned_integer_8(value: int) -> DataElement: return DataElement(DataElement.UNSIGNED_INTEGER, value, value_size=1) @staticmethod - def unsigned_integer_16(value): + def unsigned_integer_16(value: int) -> DataElement: return DataElement(DataElement.UNSIGNED_INTEGER, value, value_size=2) @staticmethod - def unsigned_integer_32(value): + def unsigned_integer_32(value: int) -> DataElement: return DataElement(DataElement.UNSIGNED_INTEGER, value, value_size=4) @staticmethod - def signed_integer(value, value_size): + def signed_integer(value: int, value_size: int) -> DataElement: return DataElement(DataElement.SIGNED_INTEGER, value, value_size) @staticmethod - def signed_integer_8(value): + def signed_integer_8(value: int) -> DataElement: return DataElement(DataElement.SIGNED_INTEGER, value, value_size=1) @staticmethod - def signed_integer_16(value): + def signed_integer_16(value: int) -> DataElement: return DataElement(DataElement.SIGNED_INTEGER, value, value_size=2) @staticmethod - def signed_integer_32(value): + def signed_integer_32(value: int) -> DataElement: return DataElement(DataElement.SIGNED_INTEGER, value, value_size=4) @staticmethod - def uuid(value): + def uuid(value: core.UUID) -> DataElement: return DataElement(DataElement.UUID, value) @staticmethod - def text_string(value): + def text_string(value: str) -> DataElement: return DataElement(DataElement.TEXT_STRING, value) @staticmethod - def boolean(value): + def boolean(value: bool) -> DataElement: return DataElement(DataElement.BOOLEAN, value) @staticmethod - def sequence(value): + def sequence(value: List[DataElement]) -> DataElement: return DataElement(DataElement.SEQUENCE, value) @staticmethod - def alternative(value): + def alternative(value: List[DataElement]) -> DataElement: return DataElement(DataElement.ALTERNATIVE, value) @staticmethod - def url(value): + def url(value: str) -> DataElement: return DataElement(DataElement.URL, value) @staticmethod def unsigned_integer_from_bytes(data): if len(data) == 1: return data[0] - elif len(data) == 2: + + if len(data) == 2: return struct.unpack('>H', data)[0] - elif len(data) == 4: + + if len(data) == 4: return struct.unpack('>I', data)[0] - elif len(data) == 8: + + if len(data) == 8: return struct.unpack('>Q', data)[0] - else: - raise ValueError(f'invalid integer length {len(data)}') + + raise ValueError(f'invalid integer length {len(data)}') @staticmethod def signed_integer_from_bytes(data): if len(data) == 1: return struct.unpack('b', data)[0] - elif len(data) == 2: + + if len(data) == 2: return struct.unpack('>h', data)[0] - elif len(data) == 4: + + if len(data) == 4: return struct.unpack('>i', data)[0] - elif len(data) == 8: + + if len(data) == 8: return struct.unpack('>q', data)[0] - else: - raise ValueError(f'invalid integer length {len(data)}') + + raise ValueError(f'invalid integer length {len(data)}') @staticmethod def list_from_bytes(data): @@ -250,7 +279,7 @@ class DataElement: while data: element = DataElement.from_bytes(data) elements.append(element) - data = data[len(bytes(element)):] + data = data[len(bytes(element)) :] return elements @staticmethod @@ -260,11 +289,11 @@ class DataElement: @staticmethod def from_bytes(data): - type = data[0] >> 3 - size_index = data[0] & 7 + element_type = data[0] >> 3 + size_index = data[0] & 7 value_offset = 0 if size_index == 0: - if type == DataElement.NIL: + if element_type == DataElement.NIL: value_size = 0 else: value_size = 1 @@ -286,16 +315,21 @@ class DataElement: value_size = struct.unpack('>I', data[1:5])[0] value_offset = 4 - value_data = data[1 + value_offset:1 + value_offset + value_size] - constructor = DataElement.type_constructors.get(type) + value_data = data[1 + value_offset : 1 + value_offset + value_size] + constructor = DataElement.type_constructors.get(element_type) if constructor: - if type == DataElement.UNSIGNED_INTEGER or type == DataElement.SIGNED_INTEGER: + if element_type in ( + DataElement.UNSIGNED_INTEGER, + DataElement.SIGNED_INTEGER, + ): result = constructor(value_data, value_size) else: result = constructor(value_data) else: - result = DataElement(type, value_data) - result.bytes = data[:1 + value_offset + value_size] # Keep a copy so we can re-serialize to an exact replica + result = DataElement(element_type, value_data) + result.bytes = data[ + : 1 + value_offset + value_size + ] # Keep a copy so we can re-serialize to an exact replica return result def to_bytes(self): @@ -311,7 +345,8 @@ class DataElement: elif self.type == DataElement.UNSIGNED_INTEGER: if self.value < 0: raise ValueError('UNSIGNED_INTEGER cannot be negative') - elif self.value_size == 1: + + if self.value_size == 1: data = struct.pack('B', self.value) elif self.value_size == 2: data = struct.pack('>H', self.value) @@ -334,11 +369,11 @@ class DataElement: raise ValueError('invalid value_size') elif self.type == DataElement.UUID: data = bytes(reversed(bytes(self.value))) - elif self.type == DataElement.TEXT_STRING or self.type == DataElement.URL: + elif self.type in (DataElement.TEXT_STRING, DataElement.URL): data = self.value.encode('utf8') elif self.type == DataElement.BOOLEAN: data = bytes([1 if self.value else 0]) - elif self.type == DataElement.SEQUENCE or self.type == DataElement.ALTERNATIVE: + elif self.type in (DataElement.SEQUENCE, DataElement.ALTERNATIVE): data = b''.join([bytes(element) for element in self.value]) else: data = self.value @@ -349,9 +384,11 @@ class DataElement: if size != 0: raise ValueError('NIL must be empty') size_index = 0 - elif (self.type == DataElement.UNSIGNED_INTEGER or - self.type == DataElement.SIGNED_INTEGER or - self.type == DataElement.UUID): + elif self.type in ( + DataElement.UNSIGNED_INTEGER, + DataElement.SIGNED_INTEGER, + DataElement.UUID, + ): if size <= 1: size_index = 0 elif size == 2: @@ -364,10 +401,12 @@ class DataElement: size_index = 4 else: raise ValueError('invalid data size') - elif (self.type == DataElement.TEXT_STRING or - self.type == DataElement.SEQUENCE or - self.type == DataElement.ALTERNATIVE or - self.type == DataElement.URL): + elif self.type in ( + DataElement.TEXT_STRING, + DataElement.SEQUENCE, + DataElement.ALTERNATIVE, + DataElement.URL, + ): if size <= 0xFF: size_index = 5 size_bytes = bytes([size]) @@ -392,11 +431,19 @@ class DataElement: type_name = name_or_number(self.TYPE_NAMES, self.type) if self.type == DataElement.NIL: value_string = '' - elif self.type == DataElement.SEQUENCE or self.type == DataElement.ALTERNATIVE: + elif self.type in (DataElement.SEQUENCE, DataElement.ALTERNATIVE): container_separator = '\n' if pretty else '' element_separator = '\n' if pretty else ',' - value_string = f'[{container_separator}{element_separator.join([element.to_string(pretty, indentation + 1 if pretty else 0) for element in self.value])}{container_separator}{prefix}]' - elif self.type == DataElement.UNSIGNED_INTEGER or self.type == DataElement.SIGNED_INTEGER: + elements = [ + element.to_string(pretty, indentation + 1 if pretty else 0) + for element in self.value + ] + value_string = ( + f'[{container_separator}' + f'{element_separator.join(elements)}' + f'{container_separator}{prefix}]' + ) + elif self.type in (DataElement.UNSIGNED_INTEGER, DataElement.SIGNED_INTEGER): value_string = f'{self.value}#{self.value_size}' elif isinstance(self.value, DataElement): value_string = self.value.to_string(pretty, indentation) @@ -410,17 +457,17 @@ class DataElement: # ----------------------------------------------------------------------------- class ServiceAttribute: - def __init__(self, id, value): - self.id = id + def __init__(self, attribute_id: int, value: DataElement) -> None: + self.id = attribute_id self.value = value @staticmethod def list_from_data_elements(elements): attribute_list = [] for i in range(0, len(elements) // 2): - attribute_id, attribute_value = elements[2 * i:2 * (i + 1)] + attribute_id, attribute_value = elements[2 * i : 2 * (i + 1)] if attribute_id.type != DataElement.UNSIGNED_INTEGER: - logger.warn('attribute ID element is not an integer') + logger.warning('attribute ID element is not an integer') continue attribute_list.append(ServiceAttribute(attribute_id.value, attribute_value)) @@ -428,30 +475,41 @@ class ServiceAttribute: @staticmethod def find_attribute_in_list(attribute_list, attribute_id): - return next((attribute.value for attribute in attribute_list if attribute.id == attribute_id), None) + return next( + ( + attribute.value + for attribute in attribute_list + if attribute.id == attribute_id + ), + None, + ) @staticmethod - def id_name(id): - return name_or_number(SDP_ATTRIBUTE_ID_NAMES, id) + def id_name(id_code): + return name_or_number(SDP_ATTRIBUTE_ID_NAMES, id_code) @staticmethod def is_uuid_in_value(uuid, value): # Find if a uuid matches a value, either directly or recursing into sequences if value.type == DataElement.UUID: return value.value == uuid - elif value.type == DataElement.SEQUENCE: + + if value.type == DataElement.SEQUENCE: for element in value.value: if ServiceAttribute.is_uuid_in_value(uuid, element): return True return False - else: - return False - def to_string(self, color=False): - if color: - return f'Attribute(id={colors.color(self.id_name(self.id),"magenta")},value={self.value})' - else: - return f'Attribute(id={self.id_name(self.id)},value={self.value})' + return False + + def to_string(self, with_colors=False): + if with_colors: + return ( + f'Attribute(id={color(self.id_name(self.id),"magenta")},' + f'value={self.value})' + ) + + return f'Attribute(id={self.id_name(self.id)},value={self.value})' def __str__(self): return self.to_string() @@ -462,11 +520,14 @@ class SDP_PDU: ''' See Bluetooth spec @ Vol 3, Part B - 4.2 PROTOCOL DATA UNIT FORMAT ''' - sdp_pdu_classes = {} + + sdp_pdu_classes: Dict[int, Type[SDP_PDU]] = {} + name = None + pdu_id = 0 @staticmethod def from_bytes(pdu): - pdu_id, transaction_id, parameters_length = struct.unpack_from('>BHH', pdu, 0) + pdu_id, transaction_id, _parameters_length = struct.unpack_from('>BHH', pdu, 0) cls = SDP_PDU.sdp_pdu_classes.get(pdu_id) if cls is None: @@ -484,13 +545,15 @@ class SDP_PDU: @staticmethod def parse_service_record_handle_list_preceded_by_count(data, offset): count = struct.unpack_from('>H', data, offset - 2)[0] - handle_list = [struct.unpack_from('>I', data, offset + x * 4)[0] for x in range(count)] + handle_list = [ + struct.unpack_from('>I', data, offset + x * 4)[0] for x in range(count) + ] return offset + count * 4, handle_list @staticmethod def parse_bytes_preceded_by_length(data, offset): length = struct.unpack_from('>H', data, offset - 2)[0] - return offset + length, data[offset:offset + length] + return offset + length, data[offset : offset + length] @staticmethod def error_name(error_code): @@ -532,7 +595,10 @@ class SDP_PDU: HCI_Object.init_from_fields(self, self.fields, kwargs) if pdu is None: parameters = HCI_Object.dict_to_bytes(kwargs, self.fields) - pdu = struct.pack('>BHH', self.pdu_id, transaction_id, len(parameters)) + parameters + pdu = ( + struct.pack('>BHH', self.pdu_id, transaction_id, len(parameters)) + + parameters + ) self.pdu = pdu self.transaction_id = transaction_id @@ -555,9 +621,7 @@ class SDP_PDU: # ----------------------------------------------------------------------------- -@SDP_PDU.subclass([ - ('error_code', {'size': 2, 'mapper': SDP_PDU.error_name}) -]) +@SDP_PDU.subclass([('error_code', {'size': 2, 'mapper': SDP_PDU.error_name})]) class SDP_ErrorResponse(SDP_PDU): ''' See Bluetooth spec @ Vol 3, Part B - 4.4.1 SDP_ErrorResponse PDU @@ -565,11 +629,13 @@ class SDP_ErrorResponse(SDP_PDU): # ----------------------------------------------------------------------------- -@SDP_PDU.subclass([ - ('service_search_pattern', DataElement.parse_from_bytes), - ('maximum_service_record_count', '>2'), - ('continuation_state', '*') -]) +@SDP_PDU.subclass( + [ + ('service_search_pattern', DataElement.parse_from_bytes), + ('maximum_service_record_count', '>2'), + ('continuation_state', '*'), + ] +) class SDP_ServiceSearchRequest(SDP_PDU): ''' See Bluetooth spec @ Vol 3, Part B - 4.5.1 SDP_ServiceSearchRequest PDU @@ -577,12 +643,17 @@ class SDP_ServiceSearchRequest(SDP_PDU): # ----------------------------------------------------------------------------- -@SDP_PDU.subclass([ - ('total_service_record_count', '>2'), - ('current_service_record_count', '>2'), - ('service_record_handle_list', SDP_PDU.parse_service_record_handle_list_preceded_by_count), - ('continuation_state', '*') -]) +@SDP_PDU.subclass( + [ + ('total_service_record_count', '>2'), + ('current_service_record_count', '>2'), + ( + 'service_record_handle_list', + SDP_PDU.parse_service_record_handle_list_preceded_by_count, + ), + ('continuation_state', '*'), + ] +) class SDP_ServiceSearchResponse(SDP_PDU): ''' See Bluetooth spec @ Vol 3, Part B - 4.5.2 SDP_ServiceSearchResponse PDU @@ -590,12 +661,14 @@ class SDP_ServiceSearchResponse(SDP_PDU): # ----------------------------------------------------------------------------- -@SDP_PDU.subclass([ - ('service_record_handle', '>4'), - ('maximum_attribute_byte_count', '>2'), - ('attribute_id_list', DataElement.parse_from_bytes), - ('continuation_state', '*') -]) +@SDP_PDU.subclass( + [ + ('service_record_handle', '>4'), + ('maximum_attribute_byte_count', '>2'), + ('attribute_id_list', DataElement.parse_from_bytes), + ('continuation_state', '*'), + ] +) class SDP_ServiceAttributeRequest(SDP_PDU): ''' See Bluetooth spec @ Vol 3, Part B - 4.6.1 SDP_ServiceAttributeRequest PDU @@ -603,11 +676,13 @@ class SDP_ServiceAttributeRequest(SDP_PDU): # ----------------------------------------------------------------------------- -@SDP_PDU.subclass([ - ('attribute_list_byte_count', '>2'), - ('attribute_list', SDP_PDU.parse_bytes_preceded_by_length), - ('continuation_state', '*') -]) +@SDP_PDU.subclass( + [ + ('attribute_list_byte_count', '>2'), + ('attribute_list', SDP_PDU.parse_bytes_preceded_by_length), + ('continuation_state', '*'), + ] +) class SDP_ServiceAttributeResponse(SDP_PDU): ''' See Bluetooth spec @ Vol 3, Part B - 4.6.2 SDP_ServiceAttributeResponse PDU @@ -615,12 +690,14 @@ class SDP_ServiceAttributeResponse(SDP_PDU): # ----------------------------------------------------------------------------- -@SDP_PDU.subclass([ - ('service_search_pattern', DataElement.parse_from_bytes), - ('maximum_attribute_byte_count', '>2'), - ('attribute_id_list', DataElement.parse_from_bytes), - ('continuation_state', '*') -]) +@SDP_PDU.subclass( + [ + ('service_search_pattern', DataElement.parse_from_bytes), + ('maximum_attribute_byte_count', '>2'), + ('attribute_id_list', DataElement.parse_from_bytes), + ('continuation_state', '*'), + ] +) class SDP_ServiceSearchAttributeRequest(SDP_PDU): ''' See Bluetooth spec @ Vol 3, Part B - 4.7.1 SDP_ServiceSearchAttributeRequest PDU @@ -628,11 +705,13 @@ class SDP_ServiceSearchAttributeRequest(SDP_PDU): # ----------------------------------------------------------------------------- -@SDP_PDU.subclass([ - ('attribute_lists_byte_count', '>2'), - ('attribute_lists', SDP_PDU.parse_bytes_preceded_by_length), - ('continuation_state', '*') -]) +@SDP_PDU.subclass( + [ + ('attribute_lists_byte_count', '>2'), + ('attribute_lists', SDP_PDU.parse_bytes_preceded_by_length), + ('continuation_state', '*'), + ] +) class SDP_ServiceSearchAttributeResponse(SDP_PDU): ''' See Bluetooth spec @ Vol 3, Part B - 4.7.2 SDP_ServiceSearchAttributeResponse PDU @@ -642,9 +721,9 @@ class SDP_ServiceSearchAttributeResponse(SDP_PDU): # ----------------------------------------------------------------------------- class Client: def __init__(self, device): - self.device = device + self.device = device self.pending_request = None - self.channel = None + self.channel = None async def connect(self, connection): result = await self.device.l2cap_channel_manager.connect(connection, SDP_PSM) @@ -659,7 +738,9 @@ class Client: if self.pending_request is not None: raise InvalidStateError('request already pending') - service_search_pattern = DataElement.sequence([DataElement.uuid(uuid) for uuid in uuids]) + service_search_pattern = DataElement.sequence( + [DataElement.uuid(uuid) for uuid in uuids] + ) # Request and accumulate until there's no more continuation service_record_handle_list = [] @@ -668,10 +749,10 @@ class Client: while watchdog > 0: response_pdu = await self.channel.send_request( SDP_ServiceSearchRequest( - transaction_id = 0, # Transaction ID TODO: pick a real value - service_search_pattern = service_search_pattern, - maximum_service_record_count = 0xFFFF, - continuation_state = continuation_state + transaction_id=0, # Transaction ID TODO: pick a real value + service_search_pattern=service_search_pattern, + maximum_service_record_count=0xFFFF, + continuation_state=continuation_state, ) ) response = SDP_PDU.from_bytes(response_pdu) @@ -689,11 +770,15 @@ class Client: if self.pending_request is not None: raise InvalidStateError('request already pending') - service_search_pattern = DataElement.sequence([DataElement.uuid(uuid) for uuid in uuids]) + service_search_pattern = DataElement.sequence( + [DataElement.uuid(uuid) for uuid in uuids] + ) attribute_id_list = DataElement.sequence( [ - DataElement.unsigned_integer(attribute_id[0], value_size=attribute_id[1]) - if type(attribute_id) is tuple + DataElement.unsigned_integer( + attribute_id[0], value_size=attribute_id[1] + ) + if isinstance(attribute_id, tuple) else DataElement.unsigned_integer_16(attribute_id) for attribute_id in attribute_ids ] @@ -706,11 +791,11 @@ class Client: while watchdog > 0: response_pdu = await self.channel.send_request( SDP_ServiceSearchAttributeRequest( - transaction_id = 0, # Transaction ID TODO: pick a real value - service_search_pattern = service_search_pattern, - maximum_attribute_byte_count = 0xFFFF, - attribute_id_list = attribute_id_list, - continuation_state = continuation_state + transaction_id=0, # Transaction ID TODO: pick a real value + service_search_pattern=service_search_pattern, + maximum_attribute_byte_count=0xFFFF, + attribute_id_list=attribute_id_list, + continuation_state=continuation_state, ) ) response = SDP_PDU.from_bytes(response_pdu) @@ -725,7 +810,7 @@ class Client: # Parse the result into attribute lists attribute_lists_sequences = DataElement.from_bytes(accumulator) if attribute_lists_sequences.type != DataElement.SEQUENCE: - logger.warn('unexpected data type') + logger.warning('unexpected data type') return [] return [ @@ -740,8 +825,10 @@ class Client: attribute_id_list = DataElement.sequence( [ - DataElement.unsigned_integer(attribute_id[0], value_size=attribute_id[1]) - if type(attribute_id) is tuple + DataElement.unsigned_integer( + attribute_id[0], value_size=attribute_id[1] + ) + if isinstance(attribute_id, tuple) else DataElement.unsigned_integer_16(attribute_id) for attribute_id in attribute_ids ] @@ -754,11 +841,11 @@ class Client: while watchdog > 0: response_pdu = await self.channel.send_request( SDP_ServiceAttributeRequest( - transaction_id = 0, # Transaction ID TODO: pick a real value - service_record_handle = service_record_handle, - maximum_attribute_byte_count = 0xFFFF, - attribute_id_list = attribute_id_list, - continuation_state = continuation_state + transaction_id=0, # Transaction ID TODO: pick a real value + service_record_handle=service_record_handle, + maximum_attribute_byte_count=0xFFFF, + attribute_id_list=attribute_id_list, + continuation_state=continuation_state, ) ) response = SDP_PDU.from_bytes(response_pdu) @@ -773,7 +860,7 @@ class Client: # Parse the result into a list of attributes attribute_list_sequence = DataElement.from_bytes(accumulator) if attribute_list_sequence.type != DataElement.SEQUENCE: - logger.warn('unexpected data type') + logger.warning('unexpected data type') return [] return ServiceAttribute.list_from_data_elements(attribute_list_sequence.value) @@ -784,8 +871,9 @@ class Server: CONTINUATION_STATE = bytes([0x01, 0x43]) def __init__(self, device): - self.device = device - self.service_records = {} # Service records maps, by record handle + self.device = device + self.service_records = {} # Service records maps, by record handle + self.channel = None self.current_response = None def register(self, l2cap_channel_manager): @@ -820,11 +908,10 @@ class Server: try: sdp_pdu = SDP_PDU.from_bytes(pdu) except Exception as error: - logger.warn(color(f'failed to parse SDP Request PDU: {error}', 'red')) + logger.warning(color(f'failed to parse SDP Request PDU: {error}', 'red')) self.send_response( SDP_ErrorResponse( - transaction_id = 0, - error_code = SDP_INVALID_REQUEST_SYNTAX_ERROR + transaction_id=0, error_code=SDP_INVALID_REQUEST_SYNTAX_ERROR ) ) @@ -840,16 +927,16 @@ class Server: logger.warning(f'{color("!!! Exception in handler:", "red")} {error}') self.send_response( SDP_ErrorResponse( - transaction_id = sdp_pdu.transaction_id, - error_code = SDP_INSUFFICIENT_RESOURCES_TO_SATISFY_REQUEST_ERROR + transaction_id=sdp_pdu.transaction_id, + error_code=SDP_INSUFFICIENT_RESOURCES_TO_SATISFY_REQUEST_ERROR, ) ) else: logger.error(color('SDP Request not handled???', 'red')) self.send_response( SDP_ErrorResponse( - transaction_id = sdp_pdu.transaction_id, - error_code = SDP_INVALID_REQUEST_SYNTAX_ERROR + transaction_id=sdp_pdu.transaction_id, + error_code=SDP_INVALID_REQUEST_SYNTAX_ERROR, ) ) @@ -872,17 +959,18 @@ class Server: if attribute_id.value_size == 4: # Attribute ID range id_range_start = attribute_id.value >> 16 - id_range_end = attribute_id.value & 0xFFFF + id_range_end = attribute_id.value & 0xFFFF else: id_range_start = attribute_id.value - id_range_end = attribute_id.value + id_range_end = attribute_id.value attributes += [ - attribute for attribute in service + attribute + for attribute in service if attribute.id >= id_range_start and attribute.id <= id_range_end ] - # Return the maching attributes, sorted by attribute id - attributes.sort(key = lambda x: x.id) + # Return the matching attributes, sorted by attribute id + attributes.sort(key=lambda x: x.id) attribute_list = DataElement.sequence([]) for attribute in attributes: attribute_list.value.append(DataElement.unsigned_integer_16(attribute.id)) @@ -896,8 +984,8 @@ class Server: if not self.current_response: self.send_response( SDP_ErrorResponse( - transaction_id = request.transaction_id, - error_code = SDP_INVALID_CONTINUATION_STATE_ERROR + transaction_id=request.transaction_id, + error_code=SDP_INVALID_CONTINUATION_STATE_ERROR, ) ) return @@ -910,30 +998,38 @@ class Server: service_record_handles = list(matching_services.keys()) # Only return up to the maximum requested - service_record_handles_subset = service_record_handles[:request.maximum_service_record_count] + service_record_handles_subset = service_record_handles[ + : request.maximum_service_record_count + ] # Serialize to a byte array, and remember the total count logger.debug(f'Service Record Handles: {service_record_handles}') self.current_response = ( len(service_record_handles), - service_record_handles_subset + service_record_handles_subset, ) # Respond, keeping any unsent handles for later - service_record_handles = self.current_response[1][:request.maximum_service_record_count] + service_record_handles = self.current_response[1][ + : request.maximum_service_record_count + ] self.current_response = ( self.current_response[0], - self.current_response[1][request.maximum_service_record_count:] + self.current_response[1][request.maximum_service_record_count :], + ) + continuation_state = ( + Server.CONTINUATION_STATE if self.current_response[1] else bytes([0]) + ) + service_record_handle_list = b''.join( + [struct.pack('>I', handle) for handle in service_record_handles] ) - continuation_state = Server.CONTINUATION_STATE if self.current_response[1] else bytes([0]) - service_record_handle_list = b''.join([struct.pack('>I', handle) for handle in service_record_handles]) self.send_response( SDP_ServiceSearchResponse( - transaction_id = request.transaction_id, - total_service_record_count = self.current_response[0], - current_service_record_count = len(service_record_handles), - service_record_handle_list = service_record_handle_list, - continuation_state = continuation_state + transaction_id=request.transaction_id, + total_service_record_count=self.current_response[0], + current_service_record_count=len(service_record_handles), + service_record_handle_list=service_record_handle_list, + continuation_state=continuation_state, ) ) @@ -943,8 +1039,8 @@ class Server: if not self.current_response: self.send_response( SDP_ErrorResponse( - transaction_id = request.transaction_id, - error_code = SDP_INVALID_CONTINUATION_STATE_ERROR + transaction_id=request.transaction_id, + error_code=SDP_INVALID_CONTINUATION_STATE_ERROR, ) ) return @@ -957,27 +1053,31 @@ class Server: if service is None: self.send_response( SDP_ErrorResponse( - transaction_id = request.transaction_id, - error_code = SDP_INVALID_SERVICE_RECORD_HANDLE_ERROR + transaction_id=request.transaction_id, + error_code=SDP_INVALID_SERVICE_RECORD_HANDLE_ERROR, ) ) return # Get the attributes for the service - attribute_list = Server.get_service_attributes(service, request.attribute_id_list.value) + attribute_list = Server.get_service_attributes( + service, request.attribute_id_list.value + ) # Serialize to a byte array logger.debug(f'Attributes: {attribute_list}') self.current_response = bytes(attribute_list) # Respond, keeping any pending chunks for later - attribute_list, continuation_state = self.get_next_response_payload(request.maximum_attribute_byte_count) + attribute_list, continuation_state = self.get_next_response_payload( + request.maximum_attribute_byte_count + ) self.send_response( SDP_ServiceAttributeResponse( - transaction_id = request.transaction_id, - attribute_list_byte_count = len(attribute_list), - attribute_list = attribute_list, - continuation_state = continuation_state + transaction_id=request.transaction_id, + attribute_list_byte_count=len(attribute_list), + attribute_list=attribute_list, + continuation_state=continuation_state, ) ) @@ -987,8 +1087,8 @@ class Server: if not self.current_response: self.send_response( SDP_ErrorResponse( - transaction_id = request.transaction_id, - error_code = SDP_INVALID_CONTINUATION_STATE_ERROR + transaction_id=request.transaction_id, + error_code=SDP_INVALID_CONTINUATION_STATE_ERROR, ) ) else: @@ -996,12 +1096,16 @@ class Server: self.current_response = None # Find the matching services - matching_services = self.match_services(request.service_search_pattern).values() + matching_services = self.match_services( + request.service_search_pattern + ).values() # Filter the required attributes attribute_lists = DataElement.sequence([]) for service in matching_services: - attribute_list = Server.get_service_attributes(service, request.attribute_id_list.value) + attribute_list = Server.get_service_attributes( + service, request.attribute_id_list.value + ) if attribute_list.value: attribute_lists.value.append(attribute_list) @@ -1010,12 +1114,14 @@ class Server: self.current_response = bytes(attribute_lists) # Respond, keeping any pending chunks for later - attribute_lists, continuation_state = self.get_next_response_payload(request.maximum_attribute_byte_count) + attribute_lists, continuation_state = self.get_next_response_payload( + request.maximum_attribute_byte_count + ) self.send_response( SDP_ServiceSearchAttributeResponse( - transaction_id = request.transaction_id, - attribute_lists_byte_count = len(attribute_lists), - attribute_lists = attribute_lists, - continuation_state = continuation_state + transaction_id=request.transaction_id, + attribute_lists_byte_count=len(attribute_lists), + attribute_lists=attribute_lists, + continuation_state=continuation_state, ) ) diff --git a/bumble/smp.py b/bumble/smp.py index dbb2b7a..f3fbf27 100644 --- a/bumble/smp.py +++ b/bumble/smp.py @@ -22,17 +22,45 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations import logging import asyncio import secrets +from typing import ( + TYPE_CHECKING, + Any, + Awaitable, + Callable, + Dict, + List, + Optional, + Tuple, + Type, +) + from pyee import EventEmitter -from colors import color -from .core import * -from .hci import * +from .colors import color +from .hci import ( + Address, + HCI_LE_Enable_Encryption_Command, + HCI_Object, + key_with_value, +) +from .core import ( + BT_BR_EDR_TRANSPORT, + BT_CENTRAL_ROLE, + BT_LE_TRANSPORT, + ProtocolError, + name_or_number, +) from .keys import PairingKeys from . import crypto +if TYPE_CHECKING: + from bumble.device import Connection, Device + from bumble.pairing import PairingConfig + # ----------------------------------------------------------------------------- # Logging @@ -43,7 +71,11 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- # Constants # ----------------------------------------------------------------------------- +# fmt: off +# pylint: disable=line-too-long + SMP_CID = 0x06 +SMP_BR_CID = 0x07 SMP_PAIRING_REQUEST_COMMAND = 0x01 SMP_PAIRING_RESPONSE_COMMAND = 0x02 @@ -152,11 +184,17 @@ SMP_CT2_AUTHREQ = 0b00100000 # Crypto salt SMP_CTKD_H7_LEBR_SALT = bytes.fromhex('00000000000000000000000000000000746D7031') +SMP_CTKD_H7_BRLE_SALT = bytes.fromhex('00000000000000000000000000000000746D7032') + +# fmt: on +# pylint: enable=line-too-long +# pylint: disable=invalid-name + # ----------------------------------------------------------------------------- # Utils # ----------------------------------------------------------------------------- -def error_name(error_code): +def error_name(error_code: int) -> str: return name_or_number(SMP_ERROR_NAMES, error_code) @@ -167,11 +205,14 @@ class SMP_Command: ''' See Bluetooth spec @ Vol 3, Part H - 3 SECURITY MANAGER PROTOCOL ''' - smp_classes = {} + + smp_classes: Dict[int, Type[SMP_Command]] = {} + fields: Any code = 0 + name = '' @staticmethod - def from_bytes(pdu): + def from_bytes(pdu: bytes) -> "SMP_Command": code = pdu[0] cls = SMP_Command.smp_classes.get(code) @@ -187,26 +228,29 @@ class SMP_Command: return self @staticmethod - def command_name(code): + def command_name(code: int) -> str: return name_or_number(SMP_COMMAND_NAMES, code) @staticmethod - def auth_req_str(value): + def auth_req_str(value: int) -> str: bonding_flags = value & 3 - mitm = (value >> 2) & 1 - sc = (value >> 3) & 1 - keypress = (value >> 4) & 1 - ct2 = (value >> 5) & 1 + mitm = (value >> 2) & 1 + sc = (value >> 3) & 1 + keypress = (value >> 4) & 1 + ct2 = (value >> 5) & 1 - return f'bonding_flags={bonding_flags}, MITM={mitm}, sc={sc}, keypress={keypress}, ct2={ct2}' + return ( + f'bonding_flags={bonding_flags}, ' + f'MITM={mitm}, sc={sc}, keypress={keypress}, ct2={ct2}' + ) @staticmethod - def io_capability_name(io_capability): + def io_capability_name(io_capability: int) -> str: return name_or_number(SMP_IO_CAPABILITY_NAMES, io_capability) @staticmethod - def key_distribution_str(value): - key_types = [] + def key_distribution_str(value: int) -> str: + key_types: List[str] = [] if value & SMP_ENC_KEY_DISTRIBUTION_FLAG: key_types.append('ENC') if value & SMP_ID_KEY_DISTRIBUTION_FLAG: @@ -218,7 +262,7 @@ class SMP_Command: return ','.join(key_types) @staticmethod - def keypress_notification_type_name(notification_type): + def keypress_notification_type_name(notification_type: int) -> str: return name_or_number(SMP_KEYPRESS_NOTIFICATION_TYPE_NAMES, notification_type) @staticmethod @@ -227,7 +271,9 @@ class SMP_Command: cls.name = cls.__name__.upper() cls.code = key_with_value(SMP_COMMAND_NAMES, cls.name) if cls.code is None: - raise KeyError(f'Command name {cls.name} not found in SMP_COMMAND_NAMES') + raise KeyError( + f'Command name {cls.name} not found in SMP_COMMAND_NAMES' + ) cls.fields = fields # Register a factory for this class @@ -237,14 +283,14 @@ class SMP_Command: return inner - def __init__(self, pdu=None, **kwargs): + def __init__(self, pdu: Optional[bytes] = None, **kwargs: Any) -> None: if hasattr(self, 'fields') and kwargs: HCI_Object.init_from_fields(self, self.fields, kwargs) if pdu is None: pdu = bytes([self.code]) + HCI_Object.dict_to_bytes(kwargs, self.fields) self.pdu = pdu - def init_from_bytes(self, pdu, offset): + def init_from_bytes(self, pdu: bytes, offset: int) -> None: return HCI_Object.init_from_bytes(self, pdu, offset, self.fields) def to_bytes(self): @@ -264,160 +310,210 @@ class SMP_Command: # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('io_capability', {'size': 1, 'mapper': SMP_Command.io_capability_name}), - ('oob_data_flag', 1), - ('auth_req', {'size': 1, 'mapper': SMP_Command.auth_req_str}), - ('maximum_encryption_key_size', 1), - ('initiator_key_distribution', {'size': 1, 'mapper': SMP_Command.key_distribution_str}), - ('responder_key_distribution', {'size': 1, 'mapper': SMP_Command.key_distribution_str}) -]) +@SMP_Command.subclass( + [ + ('io_capability', {'size': 1, 'mapper': SMP_Command.io_capability_name}), + ('oob_data_flag', 1), + ('auth_req', {'size': 1, 'mapper': SMP_Command.auth_req_str}), + ('maximum_encryption_key_size', 1), + ( + 'initiator_key_distribution', + {'size': 1, 'mapper': SMP_Command.key_distribution_str}, + ), + ( + 'responder_key_distribution', + {'size': 1, 'mapper': SMP_Command.key_distribution_str}, + ), + ] +) class SMP_Pairing_Request_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.5.1 Pairing Request ''' + io_capability: int + oob_data_flag: int + auth_req: int + maximum_encryption_key_size: int + initiator_key_distribution: int + responder_key_distribution: int + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('io_capability', {'size': 1, 'mapper': SMP_Command.io_capability_name}), - ('oob_data_flag', 1), - ('auth_req', {'size': 1, 'mapper': SMP_Command.auth_req_str}), - ('maximum_encryption_key_size', 1), - ('initiator_key_distribution', {'size': 1, 'mapper': SMP_Command.key_distribution_str}), - ('responder_key_distribution', {'size': 1, 'mapper': SMP_Command.key_distribution_str}) -]) +@SMP_Command.subclass( + [ + ('io_capability', {'size': 1, 'mapper': SMP_Command.io_capability_name}), + ('oob_data_flag', 1), + ('auth_req', {'size': 1, 'mapper': SMP_Command.auth_req_str}), + ('maximum_encryption_key_size', 1), + ( + 'initiator_key_distribution', + {'size': 1, 'mapper': SMP_Command.key_distribution_str}, + ), + ( + 'responder_key_distribution', + {'size': 1, 'mapper': SMP_Command.key_distribution_str}, + ), + ] +) class SMP_Pairing_Response_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.5.2 Pairing Response ''' + io_capability: int + oob_data_flag: int + auth_req: int + maximum_encryption_key_size: int + initiator_key_distribution: int + responder_key_distribution: int + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('confirm_value', 16) -]) +@SMP_Command.subclass([('confirm_value', 16)]) class SMP_Pairing_Confirm_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.5.3 Pairing Confirm ''' + confirm_value: bytes + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('random_value', 16) -]) +@SMP_Command.subclass([('random_value', 16)]) class SMP_Pairing_Random_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.5.4 Pairing Random ''' + random_value: bytes + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('reason', {'size': 1, 'mapper': error_name}) -]) +@SMP_Command.subclass([('reason', {'size': 1, 'mapper': error_name})]) class SMP_Pairing_Failed_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.5.5 Pairing Failed ''' + reason: int + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('public_key_x', 32), - ('public_key_y', 32) -]) +@SMP_Command.subclass([('public_key_x', 32), ('public_key_y', 32)]) class SMP_Pairing_Public_Key_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.5.6 Pairing Public Key ''' + public_key_x: bytes + public_key_y: bytes + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('dhkey_check', 16), -]) +@SMP_Command.subclass( + [ + ('dhkey_check', 16), + ] +) class SMP_Pairing_DHKey_Check_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.5.7 Pairing DHKey Check ''' + dhkey_check: bytes + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('notification_type', {'size': 1, 'mapper': SMP_Command.keypress_notification_type_name}), -]) +@SMP_Command.subclass( + [ + ( + 'notification_type', + {'size': 1, 'mapper': SMP_Command.keypress_notification_type_name}, + ), + ] +) class SMP_Pairing_Keypress_Notification_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.5.8 Keypress Notification ''' + notification_type: int + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('long_term_key', 16) -]) +@SMP_Command.subclass([('long_term_key', 16)]) class SMP_Encryption_Information_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.6.2 Encryption Information ''' + long_term_key: bytes + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('ediv', 2), - ('rand', 8) -]) +@SMP_Command.subclass([('ediv', 2), ('rand', 8)]) class SMP_Master_Identification_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.6.3 Master Identification ''' + ediv: int + rand: bytes + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('identity_resolving_key', 16) -]) +@SMP_Command.subclass([('identity_resolving_key', 16)]) class SMP_Identity_Information_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.6.4 Identity Information ''' + identity_resolving_key: bytes + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('addr_type', Address.ADDRESS_TYPE_SPEC), - ('bd_addr', Address.parse_address_preceded_by_type) -]) +@SMP_Command.subclass( + [ + ('addr_type', Address.ADDRESS_TYPE_SPEC), + ('bd_addr', Address.parse_address_preceded_by_type), + ] +) class SMP_Identity_Address_Information_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.6.5 Identity Address Information ''' + addr_type: int + bd_addr: Address + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('signature_key', 16) -]) +@SMP_Command.subclass([('signature_key', 16)]) class SMP_Signing_Information_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.6.6 Signing Information ''' + signature_key: bytes + # ----------------------------------------------------------------------------- -@SMP_Command.subclass([ - ('auth_req', {'size': 1, 'mapper': SMP_Command.auth_req_str}), -]) +@SMP_Command.subclass( + [ + ('auth_req', {'size': 1, 'mapper': SMP_Command.auth_req_str}), + ] +) class SMP_Security_Request_Command(SMP_Command): ''' See Bluetooth spec @ Vol 3, Part H - 3.6.7 Security Request ''' + auth_req: int + # ----------------------------------------------------------------------------- -def smp_auth_req(bonding, mitm, sc, keypress, ct2): +def smp_auth_req(bonding: bool, mitm: bool, sc: bool, keypress: bool, ct2: bool) -> int: value = 0 if bonding: value |= SMP_BONDING_AUTHREQ @@ -439,271 +535,259 @@ class AddressResolver: def resolve(self, address): address_bytes = bytes(address) - hash = address_bytes[0:3] + hash_part = address_bytes[0:3] prand = address_bytes[3:6] - for (irk, resolved_address) in self.resolving_keys: + for irk, resolved_address in self.resolving_keys: local_hash = crypto.ah(irk, prand) - if local_hash == hash: + if local_hash == hash_part: # Match! if resolved_address.address_type == Address.PUBLIC_DEVICE_ADDRESS: resolved_address_type = Address.PUBLIC_IDENTITY_ADDRESS else: resolved_address_type = Address.RANDOM_IDENTITY_ADDRESS - return Address(address=str(resolved_address), address_type=resolved_address_type) - - -# ----------------------------------------------------------------------------- -class PairingDelegate: - NO_OUTPUT_NO_INPUT = SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY - KEYBOARD_INPUT_ONLY = SMP_KEYBOARD_ONLY_IO_CAPABILITY - DISPLAY_OUTPUT_ONLY = SMP_DISPLAY_ONLY_IO_CAPABILITY - DISPLAY_OUTPUT_AND_YES_NO_INPUT = SMP_DISPLAY_YES_NO_IO_CAPABILITY - DISPLAY_OUTPUT_AND_KEYBOARD_INPUT = SMP_KEYBOARD_DISPLAY_IO_CAPABILITY - DEFAULT_KEY_DISTRIBUTION = (SMP_ENC_KEY_DISTRIBUTION_FLAG | SMP_ID_KEY_DISTRIBUTION_FLAG) - - def __init__( - self, - io_capability=NO_OUTPUT_NO_INPUT, - local_initiator_key_distribution=DEFAULT_KEY_DISTRIBUTION, - local_responder_key_distribution=DEFAULT_KEY_DISTRIBUTION - ): - self.io_capability = io_capability - self.local_initiator_key_distribution = local_initiator_key_distribution - self.local_responder_key_distribution = local_responder_key_distribution - - async def accept(self): - return True - - async def compare_numbers(self, number, digits=6): - return True - - async def get_number(self): - return 0 - - async def display_number(self, number, digits=6): - pass - - async def key_distribution_response(self, peer_initiator_key_distribution, peer_responder_key_distribution): - return ( - (peer_initiator_key_distribution & - self.local_initiator_key_distribution), - (peer_responder_key_distribution & - self.local_responder_key_distribution) - ) - - -# ----------------------------------------------------------------------------- -class PairingConfig: - def __init__(self, sc=True, mitm=True, bonding=True, delegate=None): - self.sc = sc - self.mitm = mitm - self.bonding = bonding - self.delegate = delegate or PairingDelegate() + return Address( + address=str(resolved_address), address_type=resolved_address_type + ) - def __str__(self): - io_capability_str = SMP_Command.io_capability_name(self.delegate.io_capability) - return f'PairingConfig(sc={self.sc}, mitm={self.mitm}, bonding={self.bonding}, delegate[{io_capability_str}])' + return None # ----------------------------------------------------------------------------- class Session: # Pairing methods - JUST_WORKS = 0 + JUST_WORKS = 0 NUMERIC_COMPARISON = 1 - PASSKEY = 2 - OOB = 3 + PASSKEY = 2 + OOB = 3 PAIRING_METHOD_NAMES = { - JUST_WORKS: 'JUST_WORKS', + JUST_WORKS: 'JUST_WORKS', NUMERIC_COMPARISON: 'NUMERIC_COMPARISON', - PASSKEY: 'PASSKEY', - OOB: 'OOB' + PASSKEY: 'PASSKEY', + OOB: 'OOB', } # I/O Capability to pairing method decision matrix # - # See Bluetooth spec @ Vol 3, part H - Table 2.8: Mapping of IO Capabilities to Key Generation Method + # See Bluetooth spec @ Vol 3, part H - Table 2.8: Mapping of IO Capabilities to Key + # Generation Method # # Map: initiator -> responder -> <method> - # where <method> may be a simple entry or a 2-element tuple, with the first element for legacy - # pairing and the second for secure connections, when the two are different. - # Each entry is either a method name, or, for PASSKEY, a tuple: + # where <method> may be a simple entry or a 2-element tuple, with the first element + # for legacy pairing and the second for secure connections, when the two are + # different. Each entry is either a method name, or, for PASSKEY, a tuple: # (method, initiator_displays, responder_displays) - # to specify if the initiator and responder should display (True) or input a code (False). + # to specify if the initiator and responder should display (True) or input a code + # (False). PAIRING_METHODS = { SMP_DISPLAY_ONLY_IO_CAPABILITY: { - SMP_DISPLAY_ONLY_IO_CAPABILITY: JUST_WORKS, - SMP_DISPLAY_YES_NO_IO_CAPABILITY: JUST_WORKS, - SMP_KEYBOARD_ONLY_IO_CAPABILITY: (PASSKEY, True, False), + SMP_DISPLAY_ONLY_IO_CAPABILITY: JUST_WORKS, + SMP_DISPLAY_YES_NO_IO_CAPABILITY: JUST_WORKS, + SMP_KEYBOARD_ONLY_IO_CAPABILITY: (PASSKEY, True, False), SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS, - SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: (PASSKEY, True, False), + SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: (PASSKEY, True, False), }, SMP_DISPLAY_YES_NO_IO_CAPABILITY: { - SMP_DISPLAY_ONLY_IO_CAPABILITY: JUST_WORKS, - SMP_DISPLAY_YES_NO_IO_CAPABILITY: (JUST_WORKS, NUMERIC_COMPARISON), - SMP_KEYBOARD_ONLY_IO_CAPABILITY: (PASSKEY, True, False), + SMP_DISPLAY_ONLY_IO_CAPABILITY: JUST_WORKS, + SMP_DISPLAY_YES_NO_IO_CAPABILITY: (JUST_WORKS, NUMERIC_COMPARISON), + SMP_KEYBOARD_ONLY_IO_CAPABILITY: (PASSKEY, True, False), SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS, - SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: ((PASSKEY, True, False), NUMERIC_COMPARISON) + SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: ( + (PASSKEY, True, False), + NUMERIC_COMPARISON, + ), }, SMP_KEYBOARD_ONLY_IO_CAPABILITY: { - SMP_DISPLAY_ONLY_IO_CAPABILITY: (PASSKEY, False, True), - SMP_DISPLAY_YES_NO_IO_CAPABILITY: (PASSKEY, False, True), - SMP_KEYBOARD_ONLY_IO_CAPABILITY: (PASSKEY, False, False), + SMP_DISPLAY_ONLY_IO_CAPABILITY: (PASSKEY, False, True), + SMP_DISPLAY_YES_NO_IO_CAPABILITY: (PASSKEY, False, True), + SMP_KEYBOARD_ONLY_IO_CAPABILITY: (PASSKEY, False, False), SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS, - SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: (PASSKEY, False, True), + SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: (PASSKEY, False, True), }, SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: { - SMP_DISPLAY_ONLY_IO_CAPABILITY: JUST_WORKS, - SMP_DISPLAY_YES_NO_IO_CAPABILITY: JUST_WORKS, - SMP_KEYBOARD_ONLY_IO_CAPABILITY: JUST_WORKS, + SMP_DISPLAY_ONLY_IO_CAPABILITY: JUST_WORKS, + SMP_DISPLAY_YES_NO_IO_CAPABILITY: JUST_WORKS, + SMP_KEYBOARD_ONLY_IO_CAPABILITY: JUST_WORKS, SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS, - SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: JUST_WORKS + SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: JUST_WORKS, }, SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: { - SMP_DISPLAY_ONLY_IO_CAPABILITY: (PASSKEY, False, True), - SMP_DISPLAY_YES_NO_IO_CAPABILITY: ((PASSKEY, False, True), NUMERIC_COMPARISON), - SMP_KEYBOARD_ONLY_IO_CAPABILITY: (PASSKEY, True, False), + SMP_DISPLAY_ONLY_IO_CAPABILITY: (PASSKEY, False, True), + SMP_DISPLAY_YES_NO_IO_CAPABILITY: ( + (PASSKEY, False, True), + NUMERIC_COMPARISON, + ), + SMP_KEYBOARD_ONLY_IO_CAPABILITY: (PASSKEY, True, False), SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY: JUST_WORKS, - SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: ((PASSKEY, True, False), NUMERIC_COMPARISON) - } + SMP_KEYBOARD_DISPLAY_IO_CAPABILITY: ( + (PASSKEY, True, False), + NUMERIC_COMPARISON, + ), + }, } - def __init__(self, manager, connection, pairing_config): - self.manager = manager - self.connection = connection - self.tk = bytes(16) - self.r = bytes(16) - self.stk = None - self.ltk = None - self.ltk_ediv = 0 - self.ltk_rand = bytes(8) - self.link_key = None - self.initiator_key_distribution = 0 - self.responder_key_distribution = 0 - self.peer_random_value = None - self.peer_public_key_x = bytes(32) - self.peer_public_key_y = bytes(32) - self.peer_ltk = None - self.peer_ediv = None - self.peer_rand = None + def __init__( + self, + manager: Manager, + connection: Connection, + pairing_config: PairingConfig, + is_initiator: bool, + ) -> None: + self.manager = manager + self.connection = connection + self.preq: Optional[bytes] = None + self.pres: Optional[bytes] = None + self.ea = None + self.eb = None + self.tk = bytes(16) + self.r = bytes(16) + self.stk = None + self.ltk = None + self.ltk_ediv = 0 + self.ltk_rand = bytes(8) + self.link_key = None + self.initiator_key_distribution: int = 0 + self.responder_key_distribution: int = 0 + self.peer_random_value: Optional[bytes] = None + self.peer_public_key_x: bytes = bytes(32) + self.peer_public_key_y = bytes(32) + self.peer_ltk = None + self.peer_ediv = None + self.peer_rand: Optional[bytes] = None self.peer_identity_resolving_key = None - self.peer_bd_addr = None - self.peer_signature_key = None - self.peer_expected_distributions = [] - self.dh_key = None - self.passkey = 0 - self.passkey_step = 0 - self.passkey_display = False - self.pairing_method = 0 - self.pairing_config = pairing_config - self.wait_before_continuing = None - self.completed = False + self.peer_bd_addr: Optional[Address] = None + self.peer_signature_key = None + self.peer_expected_distributions: List[Type[SMP_Command]] = [] + self.dh_key = None + self.confirm_value = None + self.passkey: Optional[int] = None + self.passkey_ready = asyncio.Event() + self.passkey_step = 0 + self.passkey_display = False + self.pairing_method = 0 + self.pairing_config = pairing_config + self.wait_before_continuing: Optional[asyncio.Future[None]] = None + self.completed = False + self.ctkd_task: Optional[Awaitable[None]] = None # Decide if we're the initiator or the responder - self.is_initiator = (connection.role == BT_CENTRAL_ROLE) + self.is_initiator = is_initiator self.is_responder = not self.is_initiator # Listen for connection events connection.on('disconnection', self.on_disconnection) - connection.on('connection_encryption_change', self.on_connection_encryption_change) - connection.on('connection_encryption_key_refresh', self.on_connection_encryption_key_refresh) + connection.on( + 'connection_encryption_change', self.on_connection_encryption_change + ) + connection.on( + 'connection_encryption_key_refresh', + self.on_connection_encryption_key_refresh, + ) # Create a future that can be used to wait for the session to complete if self.is_initiator: - self.pairing_result = asyncio.get_running_loop().create_future() + self.pairing_result: Optional[ + asyncio.Future[None] + ] = asyncio.get_running_loop().create_future() else: self.pairing_result = None # Key Distribution (default values before negotiation) - self.initiator_key_distribution = pairing_config.delegate.local_initiator_key_distribution - self.responder_key_distribution = pairing_config.delegate.local_responder_key_distribution + self.initiator_key_distribution = ( + pairing_config.delegate.local_initiator_key_distribution + ) + self.responder_key_distribution = ( + pairing_config.delegate.local_responder_key_distribution + ) # Authentication Requirements Flags - Vol 3, Part H, Figure 3.3 - self.bonding = pairing_config.bonding - self.sc = pairing_config.sc - self.mitm = pairing_config.mitm + self.bonding: bool = pairing_config.bonding + self.sc: bool = pairing_config.sc + self.mitm: bool = pairing_config.mitm self.keypress = False - self.ct2 = False + self.ct2: bool = False # I/O Capabilities - self.io_capability = pairing_config.delegate.io_capability + self.io_capability = pairing_config.delegate.io_capability self.peer_io_capability = SMP_NO_INPUT_NO_OUTPUT_IO_CAPABILITY # OOB (not supported yet) self.oob = False # Set up addresses + self_address = connection.self_address peer_address = connection.peer_resolvable_address or connection.peer_address if self.is_initiator: - self.ia = bytes(manager.address) - self.iat = 1 if manager.address.is_random else 0 - self.ra = bytes(peer_address) + self.ia = bytes(self_address) + self.iat = 1 if self_address.is_random else 0 + self.ra = bytes(peer_address) self.rat = 1 if peer_address.is_random else 0 else: - self.ra = bytes(manager.address) - self.rat = 1 if manager.address.is_random else 0 - self.ia = bytes(peer_address) + self.ra = bytes(self_address) + self.rat = 1 if self_address.is_random else 0 + self.ia = bytes(peer_address) self.iat = 1 if peer_address.is_random else 0 @property - def pkx(self): - return ( - bytes(reversed(self.manager.ecc_key.x)), - self.peer_public_key_x - ) + def pkx(self) -> Tuple[bytes, bytes]: + return (bytes(reversed(self.manager.ecc_key.x)), self.peer_public_key_x) @property - def pka(self): + def pka(self) -> bytes: return self.pkx[0 if self.is_initiator else 1] @property - def pkb(self): + def pkb(self) -> bytes: return self.pkx[0 if self.is_responder else 1] @property - def nx(self): - return ( - self.r, - self.peer_random_value - ) + def nx(self) -> Tuple[bytes, bytes]: + assert self.peer_random_value + return (self.r, self.peer_random_value) @property - def na(self): + def na(self) -> bytes: return self.nx[0 if self.is_initiator else 1] @property - def nb(self): + def nb(self) -> bytes: return self.nx[0 if self.is_responder else 1] @property - def auth_req(self): + def auth_req(self) -> int: return smp_auth_req(self.bonding, self.mitm, self.sc, self.keypress, self.ct2) - def get_long_term_key(self, rand, ediv): + def get_long_term_key(self, rand: bytes, ediv: int) -> Optional[bytes]: if not self.sc and not self.completed: if rand == self.ltk_rand and ediv == self.ltk_ediv: return self.stk else: return self.ltk - def decide_pairing_method(self, auth_req, initiator_io_capability, responder_io_capability): + return None + + def decide_pairing_method( + self, auth_req: int, initiator_io_capability: int, responder_io_capability: int + ) -> None: if (not self.mitm) and (auth_req & SMP_MITM_AUTHREQ == 0): self.pairing_method = self.JUST_WORKS return - details = self.PAIRING_METHODS[initiator_io_capability][responder_io_capability] - if type(details) is tuple and len(details) == 2: + details = self.PAIRING_METHODS[initiator_io_capability][responder_io_capability] # type: ignore[index] + if isinstance(details, tuple) and len(details) == 2: # One entry for legacy pairing and one for secure connections details = details[1 if self.sc else 0] - if type(details) is int: + if isinstance(details, int): # Just a method ID self.pairing_method = details else: # PASSKEY method, with a method ID and display/input flags - self.pairing_method = details[0] + self.pairing_method = details[0] self.passkey_display = details[1 if self.is_initiator else 2] - def check_expected_value(self, expected, received, error): + def check_expected_value( + self, expected: bytes, received: bytes, error: int + ) -> bool: logger.debug(f'expected={expected.hex()} got={received.hex()}') if expected != received: logger.info(color('pairing confirm/check mismatch', 'red')) @@ -711,60 +795,93 @@ class Session: return False return True - def prompt_user_for_numeric_comparison(self, code, next_steps): - async def prompt(): + def prompt_user_for_confirmation(self, next_steps: Callable[[], None]) -> None: + async def prompt() -> None: + logger.debug('ask for confirmation') + try: + response = await self.pairing_config.delegate.confirm() + if response: + next_steps() + return + except Exception as error: + logger.warning(f'exception while confirm: {error}') + + self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR) + + self.connection.abort_on('disconnection', prompt()) + + def prompt_user_for_numeric_comparison( + self, code: int, next_steps: Callable[[], None] + ) -> None: + async def prompt() -> None: logger.debug(f'verification code: {code}') try: - response = await self.pairing_config.delegate.compare_numbers(code, digits=6) + response = await self.pairing_config.delegate.compare_numbers( + code, digits=6 + ) if response: next_steps() return except Exception as error: - logger.warn(f'exception while prompting: {error}') + logger.warning(f'exception while prompting: {error}') self.send_pairing_failed(SMP_CONFIRM_VALUE_FAILED_ERROR) - asyncio.create_task(prompt()) + self.connection.abort_on('disconnection', prompt()) - def prompt_user_for_number(self, next_steps): - async def prompt(): + def prompt_user_for_number(self, next_steps: Callable[[int], None]) -> None: + async def prompt() -> None: logger.debug('prompting user for passkey') try: passkey = await self.pairing_config.delegate.get_number() + if passkey is None: + logger.debug('Passkey request rejected') + self.send_pairing_failed(SMP_PASSKEY_ENTRY_FAILED_ERROR) + return logger.debug(f'user input: {passkey}') next_steps(passkey) except Exception as error: - logger.warn(f'exception while prompting: {error}') + logger.warning(f'exception while prompting: {error}') self.send_pairing_failed(SMP_PASSKEY_ENTRY_FAILED_ERROR) - asyncio.create_task(prompt()) + self.connection.abort_on('disconnection', prompt()) - def display_passkey(self): + def display_passkey(self) -> None: # Generate random Passkey/PIN code self.passkey = secrets.randbelow(1000000) + assert self.passkey is not None logger.debug(f'Pairing PIN CODE: {self.passkey:06}') + self.passkey_ready.set() # The value of TK is computed from the PIN code if not self.sc: self.tk = self.passkey.to_bytes(16, byteorder='little') logger.debug(f'TK from passkey = {self.tk.hex()}') - asyncio.create_task(self.pairing_config.delegate.display_number(self.passkey, digits=6)) + self.connection.abort_on( + 'disconnection', + self.pairing_config.delegate.display_number(self.passkey, digits=6), + ) - def input_passkey(self, next_steps=None): + def input_passkey(self, next_steps: Optional[Callable[[], None]] = None) -> None: # Prompt the user for the passkey displayed on the peer - def after_input(passkey): + def after_input(passkey: int) -> None: self.passkey = passkey if not self.sc: self.tk = passkey.to_bytes(16, byteorder='little') logger.debug(f'TK from passkey = {self.tk.hex()}') + self.passkey_ready.set() + if next_steps is not None: next_steps() + self.prompt_user_for_number(after_input) - def display_or_input_passkey(self, next_steps=None): + def display_or_input_passkey( + self, next_steps: Optional[Callable[[], None]] = None + ) -> None: if self.passkey_display: self.display_passkey() if next_steps is not None: @@ -772,65 +889,68 @@ class Session: else: self.input_passkey(next_steps) - def send_command(self, command): + def send_command(self, command: SMP_Command) -> None: self.manager.send_command(self.connection, command) - def send_pairing_failed(self, error): - self.send_command(SMP_Pairing_Failed_Command(reason = error)) + def send_pairing_failed(self, error: int) -> None: + self.send_command(SMP_Pairing_Failed_Command(reason=error)) self.on_pairing_failure(error) - def send_pairing_request_command(self): + def send_pairing_request_command(self) -> None: self.manager.on_session_start(self) command = SMP_Pairing_Request_Command( - io_capability = self.io_capability, - oob_data_flag = 0, - auth_req = self.auth_req, - maximum_encryption_key_size = 16, - initiator_key_distribution = self.initiator_key_distribution, - responder_key_distribution = self.responder_key_distribution + io_capability=self.io_capability, + oob_data_flag=0, + auth_req=self.auth_req, + maximum_encryption_key_size=16, + initiator_key_distribution=self.initiator_key_distribution, + responder_key_distribution=self.responder_key_distribution, ) self.preq = bytes(command) self.send_command(command) - def send_pairing_response_command(self): + def send_pairing_response_command(self) -> None: response = SMP_Pairing_Response_Command( - io_capability = self.io_capability, - oob_data_flag = 0, - auth_req = self.auth_req, - maximum_encryption_key_size = 16, - initiator_key_distribution = self.initiator_key_distribution, - responder_key_distribution = self.responder_key_distribution + io_capability=self.io_capability, + oob_data_flag=0, + auth_req=self.auth_req, + maximum_encryption_key_size=16, + initiator_key_distribution=self.initiator_key_distribution, + responder_key_distribution=self.responder_key_distribution, ) self.pres = bytes(response) self.send_command(response) - def send_pairing_confirm_command(self): + def send_pairing_confirm_command(self) -> None: self.r = crypto.r() logger.debug(f'generated random: {self.r.hex()}') if self.sc: - if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON: - z = 0 - elif self.pairing_method == self.PASSKEY: - z = 0x80 + ((self.passkey >> self.passkey_step) & 1) - else: - return - if self.is_initiator: - confirm_value = crypto.f4( - self.pka, - self.pkb, - self.r, - bytes([z]) - ) - else: - confirm_value = crypto.f4( - self.pkb, - self.pka, - self.r, - bytes([z]) + async def next_steps() -> None: + if self.pairing_method in (self.JUST_WORKS, self.NUMERIC_COMPARISON): + z = 0 + elif self.pairing_method == self.PASSKEY: + # We need a passkey + await self.passkey_ready.wait() + assert self.passkey + + z = 0x80 + ((self.passkey >> self.passkey_step) & 1) + else: + return + + if self.is_initiator: + confirm_value = crypto.f4(self.pka, self.pkb, self.r, bytes([z])) + else: + confirm_value = crypto.f4(self.pkb, self.pka, self.r, bytes([z])) + + self.send_command( + SMP_Pairing_Confirm_Command(confirm_value=confirm_value) ) + + # Perform the next steps asynchronously in case we need to wait for input + self.connection.abort_on('disconnection', next_steps()) else: confirm_value = crypto.c1( self.tk, @@ -840,140 +960,203 @@ class Session: self.iat, self.rat, self.ia, - self.ra + self.ra, ) - self.send_command(SMP_Pairing_Confirm_Command(confirm_value = confirm_value)) + self.send_command(SMP_Pairing_Confirm_Command(confirm_value=confirm_value)) - def send_pairing_random_command(self): - self.send_command(SMP_Pairing_Random_Command(random_value = self.r)) + def send_pairing_random_command(self) -> None: + self.send_command(SMP_Pairing_Random_Command(random_value=self.r)) - def send_public_key_command(self): + def send_public_key_command(self) -> None: self.send_command( SMP_Pairing_Public_Key_Command( - public_key_x = bytes(reversed(self.manager.ecc_key.x)), - public_key_y = bytes(reversed(self.manager.ecc_key.y)) + public_key_x=bytes(reversed(self.manager.ecc_key.x)), + public_key_y=bytes(reversed(self.manager.ecc_key.y)), ) ) - def send_pairing_dhkey_check_command(self): + def send_pairing_dhkey_check_command(self) -> None: self.send_command( SMP_Pairing_DHKey_Check_Command( - dhkey_check = self.ea if self.is_initiator else self.eb + dhkey_check=self.ea if self.is_initiator else self.eb ) ) - def start_encryption(self, key): + def start_encryption(self, key: bytes) -> None: # We can now encrypt the connection with the short term key, so that we can # distribute the long term and/or other keys over an encrypted connection - asyncio.create_task( - self.manager.device.host.send_command( - HCI_LE_Start_Encryption_Command( - connection_handle = self.connection.handle, - random_number = bytes(8), - encrypted_diversifier = 0, - long_term_key = key - ) + self.manager.device.host.send_command_sync( + HCI_LE_Enable_Encryption_Command( # type: ignore[call-arg] + connection_handle=self.connection.handle, + random_number=bytes(8), + encrypted_diversifier=0, + long_term_key=key, ) ) - def distribute_keys(self): + async def derive_ltk(self) -> None: + link_key = await self.manager.device.get_link_key(self.connection.peer_address) + assert link_key is not None + ilk = ( + crypto.h7(salt=SMP_CTKD_H7_BRLE_SALT, w=link_key) + if self.ct2 + else crypto.h6(link_key, b'tmp2') + ) + self.ltk = crypto.h6(ilk, b'brle') + + def distribute_keys(self) -> None: # Distribute the keys as required if self.is_initiator: - if not self.sc: + # CTKD: Derive LTK from LinkKey + if ( + self.connection.transport == BT_BR_EDR_TRANSPORT + and self.initiator_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG + ): + self.ctkd_task = self.connection.abort_on( + 'disconnection', self.derive_ltk() + ) + elif not self.sc: # Distribute the LTK, EDIV and RAND if self.initiator_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG: - self.send_command(SMP_Encryption_Information_Command(long_term_key=self.ltk)) - self.send_command(SMP_Master_Identification_Command(ediv=self.ltk_ediv, rand=self.ltk_rand)) + self.send_command( + SMP_Encryption_Information_Command(long_term_key=self.ltk) + ) + self.send_command( + SMP_Master_Identification_Command( + ediv=self.ltk_ediv, rand=self.ltk_rand + ) + ) # Distribute IRK & BD ADDR if self.initiator_key_distribution & SMP_ID_KEY_DISTRIBUTION_FLAG: self.send_command( - SMP_Identity_Information_Command(identity_resolving_key=self.manager.device.irk) + SMP_Identity_Information_Command( + identity_resolving_key=self.manager.device.irk + ) + ) + self.send_command( + SMP_Identity_Address_Information_Command( + addr_type=self.connection.self_address.address_type, + bd_addr=self.connection.self_address, + ) ) - self.send_command(SMP_Identity_Address_Information_Command( - addr_type = self.manager.address.address_type, - bd_addr = self.manager.address - )) # Distribute CSRK csrk = bytes(16) # FIXME: testing if self.initiator_key_distribution & SMP_SIGN_KEY_DISTRIBUTION_FLAG: self.send_command(SMP_Signing_Information_Command(signature_key=csrk)) - + # CTKD, calculate BR/EDR link key if self.initiator_key_distribution & SMP_LINK_KEY_DISTRIBUTION_FLAG: - ilk = crypto.h7( - salt=SMP_CTKD_H7_LEBR_SALT, - w=self.ltk) if self.ct2 else crypto.h6(self.ltk, b'tmp1') + ilk = ( + crypto.h7(salt=SMP_CTKD_H7_LEBR_SALT, w=self.ltk) + if self.ct2 + else crypto.h6(self.ltk, b'tmp1') + ) self.link_key = crypto.h6(ilk, b'lebr') else: + # CTKD: Derive LTK from LinkKey + if ( + self.connection.transport == BT_BR_EDR_TRANSPORT + and self.responder_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG + ): + self.ctkd_task = self.connection.abort_on( + 'disconnection', self.derive_ltk() + ) # Distribute the LTK, EDIV and RAND - if not self.sc: + elif not self.sc: if self.responder_key_distribution & SMP_ENC_KEY_DISTRIBUTION_FLAG: - self.send_command(SMP_Encryption_Information_Command(long_term_key=self.ltk)) - self.send_command(SMP_Master_Identification_Command(ediv=self.ltk_ediv, rand=self.ltk_rand)) + self.send_command( + SMP_Encryption_Information_Command(long_term_key=self.ltk) + ) + self.send_command( + SMP_Master_Identification_Command( + ediv=self.ltk_ediv, rand=self.ltk_rand + ) + ) # Distribute IRK & BD ADDR if self.responder_key_distribution & SMP_ID_KEY_DISTRIBUTION_FLAG: self.send_command( - SMP_Identity_Information_Command(identity_resolving_key=self.manager.device.irk) + SMP_Identity_Information_Command( + identity_resolving_key=self.manager.device.irk + ) + ) + self.send_command( + SMP_Identity_Address_Information_Command( + addr_type=self.connection.self_address.address_type, + bd_addr=self.connection.self_address, + ) ) - self.send_command(SMP_Identity_Address_Information_Command( - addr_type = self.manager.address.address_type, - bd_addr = self.manager.address - )) # Distribute CSRK csrk = bytes(16) # FIXME: testing if self.responder_key_distribution & SMP_SIGN_KEY_DISTRIBUTION_FLAG: self.send_command(SMP_Signing_Information_Command(signature_key=csrk)) - + # CTKD, calculate BR/EDR link key if self.responder_key_distribution & SMP_LINK_KEY_DISTRIBUTION_FLAG: - ilk = crypto.h7( - salt=SMP_CTKD_H7_LEBR_SALT, - w=self.ltk) if self.ct2 else crypto.h6(self.ltk, b'tmp1') + ilk = ( + crypto.h7(salt=SMP_CTKD_H7_LEBR_SALT, w=self.ltk) + if self.ct2 + else crypto.h6(self.ltk, b'tmp1') + ) self.link_key = crypto.h6(ilk, b'lebr') - def compute_peer_expected_distributions(self, key_distribution_flags): + def compute_peer_expected_distributions(self, key_distribution_flags: int) -> None: # Set our expectations for what to wait for in the key distribution phase self.peer_expected_distributions = [] - if not self.sc: - if (key_distribution_flags & SMP_ENC_KEY_DISTRIBUTION_FLAG != 0): - self.peer_expected_distributions.append(SMP_Encryption_Information_Command) - self.peer_expected_distributions.append(SMP_Master_Identification_Command) - if (key_distribution_flags & SMP_ID_KEY_DISTRIBUTION_FLAG != 0): + if not self.sc and self.connection.transport == BT_LE_TRANSPORT: + if key_distribution_flags & SMP_ENC_KEY_DISTRIBUTION_FLAG != 0: + self.peer_expected_distributions.append( + SMP_Encryption_Information_Command + ) + self.peer_expected_distributions.append( + SMP_Master_Identification_Command + ) + if key_distribution_flags & SMP_ID_KEY_DISTRIBUTION_FLAG != 0: self.peer_expected_distributions.append(SMP_Identity_Information_Command) - self.peer_expected_distributions.append(SMP_Identity_Address_Information_Command) - if (key_distribution_flags & SMP_SIGN_KEY_DISTRIBUTION_FLAG != 0): + self.peer_expected_distributions.append( + SMP_Identity_Address_Information_Command + ) + if key_distribution_flags & SMP_SIGN_KEY_DISTRIBUTION_FLAG != 0: self.peer_expected_distributions.append(SMP_Signing_Information_Command) - logger.debug(f'expecting distributions: {[c.__name__ for c in self.peer_expected_distributions]}') + logger.debug( + 'expecting distributions: ' + f'{[c.__name__ for c in self.peer_expected_distributions]}' + ) - def check_key_distribution(self, command_class): + def check_key_distribution(self, command_class: Type[SMP_Command]) -> None: # First, check that the connection is encrypted if not self.connection.is_encrypted: - logger.warn(color('received key distribution on a non-encrypted connection', 'red')) + logger.warning( + color('received key distribution on a non-encrypted connection', 'red') + ) self.send_pairing_failed(SMP_UNSPECIFIED_REASON_ERROR) return # Check that this command class is expected if command_class in self.peer_expected_distributions: self.peer_expected_distributions.remove(command_class) - logger.debug(f'remaining distributions: {[c.__name__ for c in self.peer_expected_distributions]}') + logger.debug( + 'remaining distributions: ' + f'{[c.__name__ for c in self.peer_expected_distributions]}' + ) if not self.peer_expected_distributions: - # The initiator can now send its keys - if self.is_initiator: - self.distribute_keys() - - # Nothing left to expect, we're done - self.on_pairing() + self.on_peer_key_distribution_complete() else: - logger.warn(color(f'!!! unexpected key distribution command: {command_class.__name__}', 'red')) + logger.warning( + color( + '!!! unexpected key distribution command: ' + f'{command_class.__name__}', + 'red', + ) + ) self.send_pairing_failed(SMP_UNSPECIFIED_REASON_ERROR) - async def pair(self): + async def pair(self) -> None: # Start pairing as an initiator # TODO: check that this session isn't already active @@ -981,101 +1164,116 @@ class Session: self.send_pairing_request_command() # Wait for the pairing process to finish - await self.pairing_result + assert self.pairing_result + await self.connection.abort_on('disconnection', self.pairing_result) - def on_disconnection(self, reason): + def on_disconnection(self, _: int) -> None: self.connection.remove_listener('disconnection', self.on_disconnection) - self.connection.remove_listener('connection_encryption_change', self.on_connection_encryption_change) - self.connection.remove_listener('connection_encryption_key_refresh', self.on_connection_encryption_key_refresh) + self.connection.remove_listener( + 'connection_encryption_change', self.on_connection_encryption_change + ) + self.connection.remove_listener( + 'connection_encryption_key_refresh', + self.on_connection_encryption_key_refresh, + ) self.manager.on_session_end(self) - def on_connection_encryption_change(self): + def on_peer_key_distribution_complete(self) -> None: + # The initiator can now send its keys + if self.is_initiator: + self.distribute_keys() + + self.connection.abort_on('disconnection', self.on_pairing()) + + def on_connection_encryption_change(self) -> None: if self.connection.is_encrypted: if self.is_responder: # The responder distributes its keys first, the initiator later self.distribute_keys() - def on_connection_encryption_key_refresh(self): + # If we're not expecting key distributions from the peer, we're done + if not self.peer_expected_distributions: + self.on_peer_key_distribution_complete() + + def on_connection_encryption_key_refresh(self) -> None: # Do as if the connection had just been encrypted self.on_connection_encryption_change() - def on_pairing(self): + async def on_pairing(self) -> None: logger.debug('pairing complete') if self.completed: return - else: - self.completed = True + + self.completed = True if self.pairing_result is not None and not self.pairing_result.done(): self.pairing_result.set_result(None) # Use the peer address from the pairing protocol or the connection - if self.peer_bd_addr: + if self.peer_bd_addr is not None: peer_address = self.peer_bd_addr else: peer_address = self.connection.peer_address + # Wait for link key fetch and key derivation + if self.ctkd_task is not None: + await self.ctkd_task + self.ctkd_task = None + # Create an object to hold the keys keys = PairingKeys() keys.address_type = peer_address.address_type authenticated = self.pairing_method != self.JUST_WORKS - if self.sc: - keys.ltk = PairingKeys.Key( - value = self.ltk, - authenticated = authenticated - ) + if self.sc or self.connection.transport == BT_BR_EDR_TRANSPORT: + keys.ltk = PairingKeys.Key(value=self.ltk, authenticated=authenticated) else: our_ltk_key = PairingKeys.Key( - value = self.ltk, - authenticated = authenticated, - ediv = self.ltk_ediv, - rand = self.ltk_rand + value=self.ltk, + authenticated=authenticated, + ediv=self.ltk_ediv, + rand=self.ltk_rand, ) peer_ltk_key = PairingKeys.Key( - value = self.peer_ltk, - authenticated = authenticated, - ediv = self.peer_ediv, - rand = self.peer_rand + value=self.peer_ltk, + authenticated=authenticated, + ediv=self.peer_ediv, + rand=self.peer_rand, ) if self.is_initiator: - keys.ltk_central = peer_ltk_key + keys.ltk_central = peer_ltk_key keys.ltk_peripheral = our_ltk_key else: - keys.ltk_central = our_ltk_key + keys.ltk_central = our_ltk_key keys.ltk_peripheral = peer_ltk_key if self.peer_identity_resolving_key is not None: keys.irk = PairingKeys.Key( - value = self.peer_identity_resolving_key, - authenticated = authenticated + value=self.peer_identity_resolving_key, authenticated=authenticated ) if self.peer_signature_key is not None: keys.csrk = PairingKeys.Key( - value = self.peer_signature_key, - authenticated = authenticated + value=self.peer_signature_key, authenticated=authenticated ) if self.link_key is not None: keys.link_key = PairingKeys.Key( - value = self.link_key, - authenticated = authenticated + value=self.link_key, authenticated=authenticated ) - self.manager.on_pairing(self, peer_address, keys) - def on_pairing_failure(self, reason): - logger.warn(f'pairing failure ({error_name(reason)})') + def on_pairing_failure(self, reason: int) -> None: + logger.warning(f'pairing failure ({error_name(reason)})') if self.completed: return - else: - self.completed = True + + self.completed = True error = ProtocolError(reason, 'smp', error_name(reason)) if self.pairing_result is not None and not self.pairing_result.done(): self.pairing_result.set_exception(error) self.manager.on_pairing_failure(self, reason) - def on_smp_command(self, command): + def on_smp_command(self, command: SMP_Command) -> None: # Find the handler method handler_name = f'on_{command.name.lower()}' handler = getattr(self, handler_name, None) @@ -1084,15 +1282,23 @@ class Session: handler(command) except Exception as error: logger.warning(f'{color("!!! Exception in handler:", "red")} {error}') - response = SMP_Pairing_Failed_Command(reason = SMP_UNSPECIFIED_REASON_ERROR) + response = SMP_Pairing_Failed_Command( + reason=SMP_UNSPECIFIED_REASON_ERROR + ) self.send_command(response) else: logger.error(color('SMP command not handled???', 'red')) - def on_smp_pairing_request_command(self, command): - asyncio.create_task(self.on_smp_pairing_request_command_async(command)) + def on_smp_pairing_request_command( + self, command: SMP_Pairing_Request_Command + ) -> None: + self.connection.abort_on( + 'disconnection', self.on_smp_pairing_request_command_async(command) + ) - async def on_smp_pairing_request_command_async(self, command): + async def on_smp_pairing_request_command_async( + self, command: SMP_Pairing_Request_Command + ) -> None: # Check if the request should proceed accepted = await self.pairing_config.delegate.accept() if not accepted: @@ -1105,25 +1311,29 @@ class Session: # Bonding and SC require both sides to request/support it self.bonding = self.bonding and (command.auth_req & SMP_BONDING_AUTHREQ != 0) - self.sc = self.sc and (command.auth_req & SMP_SC_AUTHREQ != 0) - self.ct2 = self.ct2 and (command.auth_req & SMP_CT2_AUTHREQ != 0) + self.sc = self.sc and (command.auth_req & SMP_SC_AUTHREQ != 0) + self.ct2 = self.ct2 and (command.auth_req & SMP_CT2_AUTHREQ != 0) # Check for OOB if command.oob_data_flag != 0: - self.terminate(SMP_OOB_NOT_AVAILABLE_ERROR) + self.send_pairing_failed(SMP_OOB_NOT_AVAILABLE_ERROR) return # Decide which pairing method to use self.decide_pairing_method( - command.auth_req, - command.io_capability, - self.io_capability + command.auth_req, command.io_capability, self.io_capability + ) + logger.debug( + f'pairing method: {self.PAIRING_METHOD_NAMES[self.pairing_method]}' ) - logger.debug(f'pairing method: {self.PAIRING_METHOD_NAMES[self.pairing_method]}') # Key distribution - self.initiator_key_distribution, self.responder_key_distribution = await self.pairing_config.delegate.key_distribution_response( - command.initiator_key_distribution, command.responder_key_distribution) + ( + self.initiator_key_distribution, + self.responder_key_distribution, + ) = await self.pairing_config.delegate.key_distribution_response( + command.initiator_key_distribution, command.responder_key_distribution + ) self.compute_peer_expected_distributions(self.initiator_key_distribution) # The pairing is now starting @@ -1137,9 +1347,22 @@ class Session: # Respond self.send_pairing_response_command() - def on_smp_pairing_response_command(self, command): + # Vol 3, Part C, 5.2.2.1.3 + # CTKD over BR/EDR should happen after the connection has been encrypted, + # so when receiving pairing requests, responder should start distributing keys + if ( + self.connection.transport == BT_BR_EDR_TRANSPORT + and self.connection.is_encrypted + and self.is_responder + and accepted + ): + self.distribute_keys() + + def on_smp_pairing_response_command( + self, command: SMP_Pairing_Response_Command + ) -> None: if self.is_responder: - logger.warn(color('received pairing response as a responder', 'red')) + logger.warning(color('received pairing response as a responder', 'red')) return # Save the response @@ -1148,7 +1371,7 @@ class Session: # Bonding and SC require both sides to request/support it self.bonding = self.bonding and (command.auth_req & SMP_BONDING_AUTHREQ != 0) - self.sc = self.sc and (command.auth_req & SMP_SC_AUTHREQ != 0) + self.sc = self.sc and (command.auth_req & SMP_SC_AUTHREQ != 0) # Check for OOB if self.sc and command.oob_data_flag: @@ -1157,15 +1380,18 @@ class Session: # Decide which pairing method to use self.decide_pairing_method( - command.auth_req, - self.io_capability, - command.io_capability + command.auth_req, self.io_capability, command.io_capability + ) + logger.debug( + f'pairing method: {self.PAIRING_METHOD_NAMES[self.pairing_method]}' ) - logger.debug(f'pairing method: {self.PAIRING_METHOD_NAMES[self.pairing_method]}') # Key distribution - if (command.initiator_key_distribution & ~self.initiator_key_distribution != 0) or \ - (command.responder_key_distribution & ~self.responder_key_distribution != 0): + if ( + command.initiator_key_distribution & ~self.initiator_key_distribution != 0 + ) or ( + command.responder_key_distribution & ~self.responder_key_distribution != 0 + ): # The response isn't a subset of the request self.send_pairing_failed(SMP_INVALID_PARAMETERS_ERROR) return @@ -1175,8 +1401,8 @@ class Session: # Start phase 2 if self.sc: - if self.pairing_method == self.PASSKEY and self.passkey_display: - self.display_passkey() + if self.pairing_method == self.PASSKEY: + self.display_or_input_passkey() self.send_public_key_command() else: @@ -1185,7 +1411,9 @@ class Session: else: self.send_pairing_confirm_command() - def on_smp_pairing_confirm_command_legacy(self, command): + def on_smp_pairing_confirm_command_legacy( + self, _: SMP_Pairing_Confirm_Command + ) -> None: if self.is_initiator: self.send_pairing_random_command() else: @@ -1195,8 +1423,10 @@ class Session: else: self.send_pairing_confirm_command() - def on_smp_pairing_confirm_command_secure_connections(self, command): - if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON: + def on_smp_pairing_confirm_command_secure_connections( + self, _: SMP_Pairing_Confirm_Command + ) -> None: + if self.pairing_method in (self.JUST_WORKS, self.NUMERIC_COMPARISON): if self.is_initiator: self.r = crypto.r() self.send_pairing_random_command() @@ -1206,14 +1436,18 @@ class Session: else: self.send_pairing_confirm_command() - def on_smp_pairing_confirm_command(self, command): + def on_smp_pairing_confirm_command( + self, command: SMP_Pairing_Confirm_Command + ) -> None: self.confirm_value = command.confirm_value if self.sc: self.on_smp_pairing_confirm_command_secure_connections(command) else: self.on_smp_pairing_confirm_command_legacy(command) - def on_smp_pairing_random_command_legacy(self, command): + def on_smp_pairing_random_command_legacy( + self, command: SMP_Pairing_Random_Command + ) -> None: # Check that the confirmation values match confirm_verifier = crypto.c1( self.tk, @@ -1223,12 +1457,11 @@ class Session: self.iat, self.rat, self.ia, - self.ra + self.ra, ) + assert self.confirm_value if not self.check_expected_value( - self.confirm_value, - confirm_verifier, - SMP_CONFIRM_VALUE_FAILED_ERROR + self.confirm_value, confirm_verifier, SMP_CONFIRM_VALUE_FAILED_ERROR ): return @@ -1239,45 +1472,47 @@ class Session: else: srand = self.r mrand = command.random_value - stk = crypto.s1(self.tk, srand, mrand) - logger.debug(f'STK = {stk.hex()}') + self.stk = crypto.s1(self.tk, srand, mrand) + logger.debug(f'STK = {self.stk.hex()}') # Generate LTK self.ltk = crypto.r() if self.is_initiator: - self.start_encryption(stk) + self.start_encryption(self.stk) else: self.send_pairing_random_command() - def on_smp_pairing_random_command_secure_connections(self, command): + def on_smp_pairing_random_command_secure_connections( + self, command: SMP_Pairing_Random_Command + ) -> None: + if self.pairing_method == self.PASSKEY and self.passkey is None: + logger.warning('no passkey entered, ignoring command') + return + + # pylint: disable=too-many-return-statements if self.is_initiator: - if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON: + if self.pairing_method in (self.JUST_WORKS, self.NUMERIC_COMPARISON): + assert self.confirm_value # Check that the random value matches what was committed to earlier confirm_verifier = crypto.f4( - self.pkb, - self.pka, - command.random_value, - bytes([0]) + self.pkb, self.pka, command.random_value, bytes([0]) ) if not self.check_expected_value( - self.confirm_value, - confirm_verifier, - SMP_CONFIRM_VALUE_FAILED_ERROR + self.confirm_value, confirm_verifier, SMP_CONFIRM_VALUE_FAILED_ERROR ): return elif self.pairing_method == self.PASSKEY: + assert self.passkey and self.confirm_value # Check that the random value matches what was committed to earlier confirm_verifier = crypto.f4( self.pkb, self.pka, command.random_value, - bytes([0x80 + ((self.passkey >> self.passkey_step) & 1)]) + bytes([0x80 + ((self.passkey >> self.passkey_step) & 1)]), ) if not self.check_expected_value( - self.confirm_value, - confirm_verifier, - SMP_CONFIRM_VALUE_FAILED_ERROR + self.confirm_value, confirm_verifier, SMP_CONFIRM_VALUE_FAILED_ERROR ): return @@ -1290,20 +1525,19 @@ class Session: else: return else: - if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON: + if self.pairing_method in (self.JUST_WORKS, self.NUMERIC_COMPARISON): self.send_pairing_random_command() elif self.pairing_method == self.PASSKEY: + assert self.passkey and self.confirm_value # Check that the random value matches what was committed to earlier confirm_verifier = crypto.f4( self.pka, self.pkb, command.random_value, - bytes([0x80 + ((self.passkey >> self.passkey_step) & 1)]) + bytes([0x80 + ((self.passkey >> self.passkey_step) & 1)]), ) if not self.check_expected_value( - self.confirm_value, - confirm_verifier, - SMP_CONFIRM_VALUE_FAILED_ERROR + self.confirm_value, confirm_verifier, SMP_CONFIRM_VALUE_FAILED_ERROR ): return @@ -1324,23 +1558,25 @@ class Session: (mac_key, self.ltk) = crypto.f5(self.dh_key, self.na, self.nb, a, b) # Compute the DH Key checks - if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON: + if self.pairing_method in (self.JUST_WORKS, self.NUMERIC_COMPARISON): ra = bytes(16) rb = ra elif self.pairing_method == self.PASSKEY: + assert self.passkey ra = self.passkey.to_bytes(16, byteorder='little') rb = ra else: # OOB not implemented yet return + assert self.preq and self.pres io_cap_a = self.preq[1:4] io_cap_b = self.pres[1:4] self.ea = crypto.f6(mac_key, self.na, self.nb, rb, io_cap_a, a, b) self.eb = crypto.f6(mac_key, self.nb, self.na, ra, io_cap_b, b, a) # Next steps to be performed after possible user confirmation - def next_steps(): + def next_steps() -> None: # The initiator sends the DH Key check to the responder if self.is_initiator: self.send_pairing_dhkey_check_command() @@ -1349,99 +1585,119 @@ class Session: self.wait_before_continuing.set_result(None) # Prompt the user for confirmation if needed - if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON: + if self.pairing_method in (self.JUST_WORKS, self.NUMERIC_COMPARISON): # Compute the 6-digit code code = crypto.g2(self.pka, self.pkb, self.na, self.nb) % 1000000 - if self.pairing_method == self.NUMERIC_COMPARISON: - # Ask for user confirmation - self.wait_before_continuing = asyncio.get_running_loop().create_future() - self.prompt_user_for_numeric_comparison(code, next_steps) + # Ask for user confirmation + self.wait_before_continuing = asyncio.get_running_loop().create_future() + if self.pairing_method == self.JUST_WORKS: + self.prompt_user_for_confirmation(next_steps) else: - next_steps() + self.prompt_user_for_numeric_comparison(code, next_steps) else: next_steps() - def on_smp_pairing_random_command(self, command): + def on_smp_pairing_random_command( + self, command: SMP_Pairing_Random_Command + ) -> None: self.peer_random_value = command.random_value if self.sc: self.on_smp_pairing_random_command_secure_connections(command) else: self.on_smp_pairing_random_command_legacy(command) - def on_smp_pairing_public_key_command(self, command): + def on_smp_pairing_public_key_command( + self, command: SMP_Pairing_Public_Key_Command + ) -> None: # Store the public key so that we can compute the confirmation value later self.peer_public_key_x = command.public_key_x self.peer_public_key_y = command.public_key_y # Compute the DH key - self.dh_key = bytes(reversed(self.manager.ecc_key.dh( - bytes(reversed(command.public_key_x)), - bytes(reversed(command.public_key_y)) - ))) + self.dh_key = bytes( + reversed( + self.manager.ecc_key.dh( + bytes(reversed(command.public_key_x)), + bytes(reversed(command.public_key_y)), + ) + ) + ) logger.debug(f'DH key: {self.dh_key.hex()}') if self.is_initiator: - if self.pairing_method == self.PASSKEY: - if self.passkey_display: - self.send_pairing_confirm_command() - else: - self.input_passkey(self.send_pairing_confirm_command) + self.send_pairing_confirm_command() else: - # Send our public key back to the initiator if self.pairing_method == self.PASSKEY: - self.display_or_input_passkey(self.send_public_key_command) - else: - self.send_public_key_command() + self.display_or_input_passkey() + + # Send our public key back to the initiator + self.send_public_key_command() - if self.pairing_method == self.JUST_WORKS or self.pairing_method == self.NUMERIC_COMPARISON: + if self.pairing_method in (self.JUST_WORKS, self.NUMERIC_COMPARISON): # We can now send the confirmation value self.send_pairing_confirm_command() - def on_smp_pairing_dhkey_check_command(self, command): + def on_smp_pairing_dhkey_check_command( + self, command: SMP_Pairing_DHKey_Check_Command + ) -> None: # Check that what we received matches what we computed earlier expected = self.eb if self.is_initiator else self.ea + assert expected if not self.check_expected_value( - expected, - command.dhkey_check, - SMP_DHKEY_CHECK_FAILED_ERROR + expected, command.dhkey_check, SMP_DHKEY_CHECK_FAILED_ERROR ): return if self.is_responder: if self.wait_before_continuing is not None: - async def next_steps(): + + async def next_steps() -> None: + assert self.wait_before_continuing await self.wait_before_continuing self.wait_before_continuing = None self.send_pairing_dhkey_check_command() - asyncio.create_task(next_steps()) + self.connection.abort_on('disconnection', next_steps()) else: self.send_pairing_dhkey_check_command() else: + assert self.ltk self.start_encryption(self.ltk) - def on_smp_pairing_failed_command(self, command): + def on_smp_pairing_failed_command( + self, command: SMP_Pairing_Failed_Command + ) -> None: self.on_pairing_failure(command.reason) - def on_smp_encryption_information_command(self, command): + def on_smp_encryption_information_command( + self, command: SMP_Encryption_Information_Command + ) -> None: self.peer_ltk = command.long_term_key self.check_key_distribution(SMP_Encryption_Information_Command) - def on_smp_master_identification_command(self, command): + def on_smp_master_identification_command( + self, command: SMP_Master_Identification_Command + ) -> None: self.peer_ediv = command.ediv self.peer_rand = command.rand self.check_key_distribution(SMP_Master_Identification_Command) - def on_smp_identity_information_command(self, command): + def on_smp_identity_information_command( + self, command: SMP_Identity_Information_Command + ) -> None: self.peer_identity_resolving_key = command.identity_resolving_key self.check_key_distribution(SMP_Identity_Information_Command) - def on_smp_identity_address_information_command(self, command): + def on_smp_identity_address_information_command( + self, command: SMP_Identity_Address_Information_Command + ) -> None: self.peer_bd_addr = command.bd_addr self.check_key_distribution(SMP_Identity_Address_Information_Command) - def on_smp_signing_information_command(self, command): + def on_smp_signing_information_command( + self, command: SMP_Signing_Information_Command + ) -> None: self.peer_signature_key = command.signature_key self.check_key_distribution(SMP_Signing_Information_Command) @@ -1452,57 +1708,71 @@ class Manager(EventEmitter): Implements the Initiator and Responder roles of the Security Manager Protocol ''' - def __init__(self, device, address): - super().__init__() - self.device = device - self.address = address - self.sessions = {} - self._ecc_key = None - self.pairing_config_factory = lambda connection: PairingConfig() + device: Device + sessions: Dict[int, Session] + pairing_config_factory: Callable[[Connection], PairingConfig] + session_proxy: Type[Session] - def send_command(self, connection, command): - logger.debug(f'>>> Sending SMP Command on connection [0x{connection.handle:04X}] {connection.peer_address}: {command}') - connection.send_l2cap_pdu(SMP_CID, command.to_bytes()) + def __init__( + self, + device: Device, + pairing_config_factory: Callable[[Connection], PairingConfig], + ) -> None: + super().__init__() + self.device = device + self.sessions = {} + self._ecc_key = None + self.pairing_config_factory = pairing_config_factory + self.session_proxy = Session + + def send_command(self, connection: Connection, command: SMP_Command) -> None: + logger.debug( + f'>>> Sending SMP Command on connection [0x{connection.handle:04X}] ' + f'{connection.peer_address}: {command}' + ) + cid = SMP_BR_CID if connection.transport == BT_BR_EDR_TRANSPORT else SMP_CID + connection.send_l2cap_pdu(cid, command.to_bytes()) - def on_smp_pdu(self, connection, pdu): + def on_smp_pdu(self, connection: Connection, pdu: bytes) -> None: # Look for a session with this connection, and create one if none exists if not (session := self.sessions.get(connection.handle)): + if connection.role == BT_CENTRAL_ROLE: + logger.warning('Remote starts pairing as Peripheral!') pairing_config = self.pairing_config_factory(connection) - if pairing_config is None: - # Pairing disabled - self.send_command( - connection, - SMP_Pairing_Failed_Command( - reason = SMP_PAIRING_NOT_SUPPORTED_ERROR - ) - ) - return - session = Session(self, connection, pairing_config) + session = self.session_proxy( + self, connection, pairing_config, is_initiator=False + ) self.sessions[connection.handle] = session # Parse the L2CAP payload into an SMP Command object command = SMP_Command.from_bytes(pdu) - logger.debug(f'<<< Received SMP Command on connection [0x{connection.handle:04X}] {connection.peer_address}: {command}') + logger.debug( + f'<<< Received SMP Command on connection [0x{connection.handle:04X}] ' + f'{connection.peer_address}: {command}' + ) # Delegate the handling of the command to the session session.on_smp_command(command) @property - def ecc_key(self): + def ecc_key(self) -> crypto.EccKey: if self._ecc_key is None: self._ecc_key = crypto.EccKey.generate() + assert self._ecc_key return self._ecc_key - async def pair(self, connection): + async def pair(self, connection: Connection) -> None: # TODO: check if there's already a session for this connection + if connection.role != BT_CENTRAL_ROLE: + logger.warning('Start pairing as Peripheral!') pairing_config = self.pairing_config_factory(connection) - if pairing_config is None: - raise ValueError('pairing config must not be None when initiating') - session = Session(self, connection, pairing_config) + session = self.session_proxy( + self, connection, pairing_config, is_initiator=True + ) self.sessions[connection.handle] = session return await session.pair() - def request_pairing(self, connection): + def request_pairing(self, connection: Connection) -> None: pairing_config = self.pairing_config_factory(connection) if pairing_config: auth_req = smp_auth_req( @@ -1510,36 +1780,45 @@ class Manager(EventEmitter): pairing_config.mitm, pairing_config.sc, False, - False + False, ) else: auth_req = 0 self.send_command(connection, SMP_Security_Request_Command(auth_req=auth_req)) - def on_session_start(self, session): - self.device.on_pairing_start(session.connection.handle) + def on_session_start(self, session: Session) -> None: + self.device.on_pairing_start(session.connection) - def on_pairing(self, session, identity_address, keys): + def on_pairing( + self, session: Session, identity_address: Optional[Address], keys: PairingKeys + ) -> None: # Store the keys in the key store if self.device.keystore and identity_address is not None: + async def store_keys(): try: + assert self.device.keystore await self.device.keystore.update(str(identity_address), keys) except Exception as error: - logger.warn(f'!!! error while storing keys: {error}') - asyncio.create_task(store_keys()) + logger.warning(f'!!! error while storing keys: {error}') + + self.device.abort_on('flush', store_keys()) # Notify the device - self.device.on_pairing(session.connection.handle, keys) + self.device.on_pairing(session.connection, keys, session.sc) - def on_pairing_failure(self, session, reason): - self.device.on_pairing_failure(session.connection.handle, reason) + def on_pairing_failure(self, session: Session, reason: int) -> None: + self.device.on_pairing_failure(session.connection, reason) - def on_session_end(self, session): + def on_session_end(self, session: Session) -> None: logger.debug(f'session end for connection 0x{session.connection.handle:04X}') if session.connection.handle in self.sessions: del self.sessions[session.connection.handle] - def get_long_term_key(self, connection, rand, ediv): + def get_long_term_key( + self, connection: Connection, rand: bytes, ediv: int + ) -> Optional[bytes]: if session := self.sessions.get(connection.handle): return session.get_long_term_key(rand, ediv) + + return None diff --git a/bumble/snoop.py b/bumble/snoop.py new file mode 100644 index 0000000..4b331d2 --- /dev/null +++ b/bumble/snoop.py @@ -0,0 +1,170 @@ +# Copyright 2021-2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +from contextlib import contextmanager +from enum import IntEnum +import logging +import struct +import datetime +from typing import BinaryIO, Generator +import os + +from bumble.hci import HCI_COMMAND_PACKET, HCI_EVENT_PACKET + + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- +logger = logging.getLogger(__name__) + + +# ----------------------------------------------------------------------------- +# Classes +# ----------------------------------------------------------------------------- +class Snooper: + """ + Base class for snooper implementations. + + A snooper is an object that will be provided with HCI packets as they are + exchanged between a host and a controller. + """ + + class Direction(IntEnum): + HOST_TO_CONTROLLER = 0 + CONTROLLER_TO_HOST = 1 + + class DataLinkType(IntEnum): + H1 = 1001 + H4 = 1002 + HCI_BSCP = 1003 + H5 = 1004 + + def snoop(self, hci_packet: bytes, direction: Direction) -> None: + """Snoop on an HCI packet.""" + + +# ----------------------------------------------------------------------------- +class BtSnooper(Snooper): + """ + Snooper that saves HCI packets using the BTSnoop format, based on RFC 1761. + """ + + IDENTIFICATION_PATTERN = b'btsnoop\0' + TIMESTAMP_ANCHOR = datetime.datetime(2000, 1, 1) + TIMESTAMP_DELTA = 0x00E03AB44A676000 + ONE_MS = datetime.timedelta(microseconds=1) + + def __init__(self, output: BinaryIO): + self.output = output + + # Write the header + self.output.write( + self.IDENTIFICATION_PATTERN + struct.pack('>LL', 1, self.DataLinkType.H4) + ) + + def snoop(self, hci_packet: bytes, direction: Snooper.Direction) -> None: + flags = int(direction) + packet_type = hci_packet[0] + if packet_type in (HCI_EVENT_PACKET, HCI_COMMAND_PACKET): + flags |= 0x10 + + # Compute the current timestamp + timestamp = ( + int((datetime.datetime.utcnow() - self.TIMESTAMP_ANCHOR) / self.ONE_MS) + + self.TIMESTAMP_DELTA + ) + + # Emit the record + self.output.write( + struct.pack( + '>IIIIQ', + len(hci_packet), # Original Length + len(hci_packet), # Included Length + flags, # Packet Flags + 0, # Cumulative Drops + timestamp, # Timestamp + ) + + hci_packet + ) + + +# ----------------------------------------------------------------------------- +_SNOOPER_INSTANCE_COUNT = 0 + + +@contextmanager +def create_snooper(spec: str) -> Generator[Snooper, None, None]: + """ + Create a snooper given a specification string. + + The general syntax for the specification string is: + <snooper-type>:<type-specific-arguments> + + Supported snooper types are: + + btsnoop + The syntax for the type-specific arguments for this type is: + <io-type>:<io-type-specific-arguments> + + Supported I/O types are: + + file + The type-specific arguments for this I/O type is a string that is converted + to a file path using the python `str.format()` string formatting. The log + records will be written to that file if it can be opened/created. + The keyword args that may be referenced by the string pattern are: + now: the value of `datetime.now()` + utcnow: the value of `datetime.utcnow()` + pid: the current process ID. + instance: the instance ID in the current process. + + Examples: + btsnoop:file:my_btsnoop.log + btsnoop:file:/tmp/bumble_{now:%Y-%m-%d-%H:%M:%S}_{pid}.log + + """ + if ':' not in spec: + raise ValueError('snooper type prefix missing') + + snooper_type, snooper_args = spec.split(':', maxsplit=1) + + if snooper_type == 'btsnoop': + if ':' not in snooper_args: + raise ValueError('I/O type for btsnoop snooper type missing') + + io_type, io_name = snooper_args.split(':', maxsplit=1) + if io_type == 'file': + # Process the file name string pattern. + global _SNOOPER_INSTANCE_COUNT + file_path = io_name.format( + now=datetime.datetime.now(), + utcnow=datetime.datetime.utcnow(), + pid=os.getpid(), + instance=_SNOOPER_INSTANCE_COUNT, + ) + + # Open the file + logger.debug(f'Snoop file: {file_path}') + with open(file_path, 'wb') as snoop_file: + _SNOOPER_INSTANCE_COUNT += 1 + yield BtSnooper(snoop_file) + _SNOOPER_INSTANCE_COUNT -= 1 + return + + raise ValueError(f'I/O type {io_type} not supported') + + raise ValueError(f'snooper type {snooper_type} not found') diff --git a/bumble/transport/__init__.py b/bumble/transport/__init__.py index c3bd5f8..840b3e5 100644 --- a/bumble/transport/__init__.py +++ b/bumble/transport/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Google LLC +# Copyright 2021-2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,11 +15,13 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from contextlib import asynccontextmanager import logging +import os -from .common import Transport, AsyncPipeSink -from ..link import RemoteLink +from .common import Transport, AsyncPipeSink, SnoopingTransport from ..controller import Controller +from ..snoop import create_snooper # ----------------------------------------------------------------------------- # Logging @@ -28,68 +30,153 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- -async def open_transport(name): - ''' +def _wrap_transport(transport: Transport) -> Transport: + """ + Automatically wrap a Transport instance when a wrapping class can be inferred + from the environment. + If no wrapping class is applicable, the transport argument is returned as-is. + """ + + # If BUMBLE_SNOOPER is set, try to automatically create a snooper. + if snooper_spec := os.getenv('BUMBLE_SNOOPER'): + try: + return SnoopingTransport.create_with( + transport, create_snooper(snooper_spec) + ) + except Exception as exc: + logger.warning(f'Exception while creating snooper: {exc}') + + return transport + + +# ----------------------------------------------------------------------------- +async def open_transport(name: str) -> Transport: + """ Open a transport by name. The name must be <type>:<parameters> Where <parameters> depend on the type (and may be empty for some types). - The supported types are: serial,udp,tcp,pty,usb - ''' + The supported types are: + * serial + * udp + * tcp-client + * tcp-server + * ws-client + * ws-server + * pty + * file + * vhci + * hci-socket + * usb + * pyusb + * android-emulator + """ + + return _wrap_transport(await _open_transport(name)) + + +# ----------------------------------------------------------------------------- +async def _open_transport(name: str) -> Transport: + # pylint: disable=import-outside-toplevel + # pylint: disable=too-many-return-statements + scheme, *spec = name.split(':', 1) if scheme == 'serial' and spec: from .serial import open_serial_transport + return await open_serial_transport(spec[0]) - elif scheme == 'udp' and spec: + + if scheme == 'udp' and spec: from .udp import open_udp_transport + return await open_udp_transport(spec[0]) - elif scheme == 'tcp-client' and spec: + + if scheme == 'tcp-client' and spec: from .tcp_client import open_tcp_client_transport + return await open_tcp_client_transport(spec[0]) - elif scheme == 'tcp-server' and spec: + + if scheme == 'tcp-server' and spec: from .tcp_server import open_tcp_server_transport + return await open_tcp_server_transport(spec[0]) - elif scheme == 'ws-client' and spec: + + if scheme == 'ws-client' and spec: from .ws_client import open_ws_client_transport + return await open_ws_client_transport(spec[0]) - elif scheme == 'ws-server' and spec: + + if scheme == 'ws-server' and spec: from .ws_server import open_ws_server_transport + return await open_ws_server_transport(spec[0]) - elif scheme == 'pty': + + if scheme == 'pty': from .pty import open_pty_transport + return await open_pty_transport(spec[0] if spec else None) - elif scheme == 'file': + + if scheme == 'file': from .file import open_file_transport + return await open_file_transport(spec[0] if spec else None) - elif scheme == 'vhci': + + if scheme == 'vhci': from .vhci import open_vhci_transport + return await open_vhci_transport(spec[0] if spec else None) - elif scheme == 'hci-socket': + + if scheme == 'hci-socket': from .hci_socket import open_hci_socket_transport + return await open_hci_socket_transport(spec[0] if spec else None) - elif scheme == 'usb': + + if scheme == 'usb': from .usb import open_usb_transport + return await open_usb_transport(spec[0] if spec else None) - elif scheme == 'pyusb': + + if scheme == 'pyusb': from .pyusb import open_pyusb_transport + return await open_pyusb_transport(spec[0] if spec else None) - elif scheme == 'android-emulator': + + if scheme == 'android-emulator': from .android_emulator import open_android_emulator_transport + return await open_android_emulator_transport(spec[0] if spec else None) - else: - raise ValueError('unknown transport scheme') + + if scheme == 'android-netsim': + from .android_netsim import open_android_netsim_transport + + return await open_android_netsim_transport(spec[0] if spec else None) + + raise ValueError('unknown transport scheme') # ----------------------------------------------------------------------------- -async def open_transport_or_link(name): +async def open_transport_or_link(name: str) -> Transport: + """ + Open a transport or a link relay. + + Args: + name: + Name of the transport or link relay to open. + When the name starts with "link-relay:", open a link relay (see RemoteLink + for details on what the arguments are). + For other namespaces, see `open_transport`. + + """ if name.startswith('link-relay:'): + from ..link import RemoteLink # lazy import + link = RemoteLink(name[11:]) await link.wait_until_connected() - controller = Controller('remote', link = link) + controller = Controller('remote', link=link) class LinkTransport(Transport): async def close(self): link.close() - return LinkTransport(controller, AsyncPipeSink(controller)) - else: - return await open_transport(name) + return _wrap_transport(LinkTransport(controller, AsyncPipeSink(controller))) + + return await open_transport(name) diff --git a/bumble/transport/android_emulator.py b/bumble/transport/android_emulator.py index d27aef6..b78e263 100644 --- a/bumble/transport/android_emulator.py +++ b/bumble/transport/android_emulator.py @@ -1,4 +1,4 @@ -# Copyright 2021-2022 Google LLC +# Copyright 2021-2023 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,12 +16,14 @@ # Imports # ----------------------------------------------------------------------------- import logging -import grpc +import grpc.aio from .common import PumpedTransport, PumpedPacketSource, PumpedPacketSink -from .emulated_bluetooth_pb2_grpc import EmulatedBluetoothServiceStub -from .emulated_bluetooth_packets_pb2 import HCIPacket -from .emulated_bluetooth_vhci_pb2_grpc import VhciForwardingServiceStub + +# pylint: disable=no-name-in-module +from .grpc_protobuf.emulated_bluetooth_pb2_grpc import EmulatedBluetoothServiceStub +from .grpc_protobuf.emulated_bluetooth_packets_pb2 import HCIPacket +from .grpc_protobuf.emulated_bluetooth_vhci_pb2_grpc import VhciForwardingServiceStub # ----------------------------------------------------------------------------- @@ -59,15 +61,10 @@ async def open_android_emulator_transport(spec): return bytes([packet.type]) + packet.packet async def write(self, packet): - await self.hci_device.write( - HCIPacket( - type = packet[0], - packet = packet[1:] - ) - ) + await self.hci_device.write(HCIPacket(type=packet[0], packet=packet[1:])) # Parse the parameters - mode = 'host' + mode = 'host' server_host = 'localhost' server_port = 8554 if spec is not None: @@ -100,7 +97,7 @@ async def open_android_emulator_transport(spec): transport = PumpedTransport( PumpedPacketSource(hci_device.read), PumpedPacketSink(hci_device.write), - channel.close + channel.close, ) transport.start() diff --git a/bumble/transport/android_netsim.py b/bumble/transport/android_netsim.py new file mode 100644 index 0000000..99ebf87 --- /dev/null +++ b/bumble/transport/android_netsim.py @@ -0,0 +1,410 @@ +# Copyright 2021-2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import asyncio +import atexit +import logging +import grpc.aio +import os +import pathlib +import sys +from typing import Optional + +from .common import ( + ParserSource, + PumpedTransport, + PumpedPacketSource, + PumpedPacketSink, + Transport, +) + +# pylint: disable=no-name-in-module +from .grpc_protobuf.packet_streamer_pb2_grpc import PacketStreamerStub +from .grpc_protobuf.packet_streamer_pb2_grpc import ( + PacketStreamerServicer, + add_PacketStreamerServicer_to_server, +) +from .grpc_protobuf.packet_streamer_pb2 import PacketRequest, PacketResponse +from .grpc_protobuf.hci_packet_pb2 import HCIPacket +from .grpc_protobuf.startup_pb2 import Chip, ChipInfo +from .grpc_protobuf.common_pb2 import ChipKind + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- +logger = logging.getLogger(__name__) + + +# ----------------------------------------------------------------------------- +# Constants +# ----------------------------------------------------------------------------- +DEFAULT_NAME = 'bumble0' +DEFAULT_MANUFACTURER = 'Bumble' + + +# ----------------------------------------------------------------------------- +def get_ini_dir() -> Optional[pathlib.Path]: + if sys.platform == 'darwin': + if tmpdir := os.getenv('TMPDIR', None): + return pathlib.Path(tmpdir) + if home := os.getenv('HOME', None): + return pathlib.Path(home) / 'Library/Caches/TemporaryItems' + elif sys.platform == 'linux': + if xdg_runtime_dir := os.environ.get('XDG_RUNTIME_DIR', None): + return pathlib.Path(xdg_runtime_dir) + elif sys.platform == 'win32': + if local_app_data_dir := os.environ.get('LOCALAPPDATA', None): + return pathlib.Path(local_app_data_dir) / 'Temp' + + return None + + +# ----------------------------------------------------------------------------- +def find_grpc_port() -> int: + if not (ini_dir := get_ini_dir()): + logger.debug('no known directory for .ini file') + return 0 + + ini_file = ini_dir / 'netsim.ini' + if ini_file.is_file(): + logger.debug(f'Found .ini file at {ini_file}') + with open(ini_file, 'r') as ini_file_data: + for line in ini_file_data.readlines(): + if '=' in line: + key, value = line.split('=') + if key == 'grpc.port': + logger.debug(f'gRPC port = {value}') + return int(value) + + # Not found + return 0 + + +# ----------------------------------------------------------------------------- +def publish_grpc_port(grpc_port) -> bool: + if not (ini_dir := get_ini_dir()): + logger.debug('no known directory for .ini file') + return False + + if not ini_dir.is_dir(): + logger.debug('ini directory does not exist') + return False + + ini_file = ini_dir / 'netsim.ini' + try: + ini_file.write_text(f'grpc.port={grpc_port}\n') + logger.debug(f"published gRPC port at {ini_file}") + + def cleanup(): + logger.debug("removing .ini file") + ini_file.unlink() + + atexit.register(cleanup) + return True + except OSError: + logger.debug('failed to write to .ini file') + return False + + +# ----------------------------------------------------------------------------- +async def open_android_netsim_controller_transport(server_host, server_port): + if not server_port: + raise ValueError('invalid port') + if server_host == '_' or not server_host: + server_host = 'localhost' + + if not publish_grpc_port(server_port): + logger.warning("unable to publish gRPC port") + + class HciDevice: + def __init__(self, context, on_data_received): + self.context = context + self.on_data_received = on_data_received + self.name = None + self.loop = asyncio.get_running_loop() + self.done = self.loop.create_future() + self.task = self.loop.create_task(self.pump()) + + async def pump(self): + try: + await self.pump_loop() + except asyncio.CancelledError: + logger.debug('Pump task canceled') + self.done.set_result(None) + + async def pump_loop(self): + while True: + request = await self.context.read() + if request == grpc.aio.EOF: + logger.debug('End of request stream') + self.done.set_result(None) + return + + # If we're not initialized yet, wait for a init packet. + if self.name is None: + if request.WhichOneof('request_type') == 'initial_info': + logger.debug(f'Received initial info: {request}') + + # We only accept BLUETOOTH + if request.initial_info.chip.kind != ChipKind.BLUETOOTH: + logger.warning('Unsupported chip type') + error = PacketResponse(error='Unsupported chip type') + await self.context.write(error) + return + + self.name = request.initial_info.name + continue + + # Expect a data packet + request_type = request.WhichOneof('request_type') + if request_type != 'hci_packet': + logger.warning(f'Unexpected request type: {request_type}') + error = PacketResponse(error='Unexpected request type') + await self.context.write(error) + continue + + # Process the packet + data = ( + bytes([request.hci_packet.packet_type]) + request.hci_packet.packet + ) + logger.debug(f'<<< PACKET: {data.hex()}') + self.on_data_received(data) + + def send_packet(self, data): + async def send(): + await self.context.write( + PacketResponse( + hci_packet=HCIPacket(packet_type=data[0], packet=data[1:]) + ) + ) + + self.loop.create_task(send()) + + def terminate(self): + self.task.cancel() + + async def wait_for_termination(self): + await self.done + + class Server(PacketStreamerServicer, ParserSource): + def __init__(self): + PacketStreamerServicer.__init__(self) + ParserSource.__init__(self) + self.device = None + + # Create a gRPC server with `so_reuseport=0` so that if there's already + # a server listening on that port, we get an exception. + self.grpc_server = grpc.aio.server(options=(('grpc.so_reuseport', 0),)) + add_PacketStreamerServicer_to_server(self, self.grpc_server) + self.grpc_server.add_insecure_port(f'{server_host}:{server_port}') + logger.debug(f'gRPC server listening on {server_host}:{server_port}') + + async def start(self): + logger.debug('Starting gRPC server') + await self.grpc_server.start() + + async def serve(self): + # Keep serving until terminated. + try: + await self.grpc_server.wait_for_termination() + logger.debug('gRPC server terminated') + except asyncio.CancelledError: + logger.debug('gRPC server cancelled') + await self.grpc_server.stop(None) + + def on_packet(self, packet): + if not self.device: + logger.debug('no device, dropping packet') + return + + self.device.send_packet(packet) + + async def StreamPackets(self, _request_iterator, context): + logger.debug('StreamPackets request') + + # Check that we won't already have a device + if self.device: + logger.debug('busy, already serving a device') + return PacketResponse(error='Busy') + + # Instantiate a new device + self.device = HciDevice(context, self.parser.feed_data) + + # Wait for the device to terminate + logger.debug('Waiting for device to terminate') + try: + await self.device.wait_for_termination() + except asyncio.CancelledError: + logger.debug('Request canceled') + self.device.terminate() + + logger.debug('Device terminated') + self.device = None + + server = Server() + await server.start() + asyncio.get_running_loop().create_task(server.serve()) + + class GrpcServerTransport(Transport): + async def close(self): + await super().close() + + return GrpcServerTransport(server, server) + + +# ----------------------------------------------------------------------------- +async def open_android_netsim_host_transport(server_host, server_port, options): + # Wrapper for I/O operations + class HciDevice: + def __init__(self, name, manufacturer, hci_device): + self.name = name + self.manufacturer = manufacturer + self.hci_device = hci_device + + async def start(self): # Send the startup info + chip_info = ChipInfo( + name=self.name, + chip=Chip(kind=ChipKind.BLUETOOTH, manufacturer=self.manufacturer), + ) + logger.debug(f'Sending chip info to netsim: {chip_info}') + await self.hci_device.write(PacketRequest(initial_info=chip_info)) + + async def read(self): + response = await self.hci_device.read() + response_type = response.WhichOneof('response_type') + if response_type == 'error': + logger.warning(f'received error: {response.error}') + raise RuntimeError(response.error) + elif response_type == 'hci_packet': + return ( + bytes([response.hci_packet.packet_type]) + + response.hci_packet.packet + ) + + raise ValueError('unsupported response type') + + async def write(self, packet): + await self.hci_device.write( + PacketRequest( + hci_packet=HCIPacket(packet_type=packet[0], packet=packet[1:]) + ) + ) + + name = options.get('name', DEFAULT_NAME) + manufacturer = DEFAULT_MANUFACTURER + + if server_host == '_' or not server_host: + server_host = 'localhost' + + if not server_port: + # Look for the gRPC config in a .ini file + server_host = 'localhost' + server_port = find_grpc_port() + if not server_port: + raise RuntimeError('gRPC server port not found') + + # Connect to the gRPC server + server_address = f'{server_host}:{server_port}' + logger.debug(f'Connecting to gRPC server at {server_address}') + channel = grpc.aio.insecure_channel(server_address) + + # Connect as a host + service = PacketStreamerStub(channel) + hci_device = HciDevice( + name=name, + manufacturer=manufacturer, + hci_device=service.StreamPackets(), + ) + await hci_device.start() + + # Create the transport object + transport = PumpedTransport( + PumpedPacketSource(hci_device.read), + PumpedPacketSink(hci_device.write), + channel.close, + ) + transport.start() + + return transport + + +# ----------------------------------------------------------------------------- +async def open_android_netsim_transport(spec): + ''' + Open a transport connection as a client or server, implementing Android's `netsim` + simulator protocol over gRPC. + The parameter string has this syntax: + [<host>:<port>][<options>] + Where <options> is a ','-separated list of <name>=<value> pairs. + + General options: + mode=host|controller (default: host) + Specifies whether the transport is used + to connect *to* a netsim server (netsim is the controller), or accept + connections *as* a netsim-compatible server. + + In `host` mode: + The <host>:<port> part is optional. When not specified, the transport + looks for a netsim .ini file, from which it will read the `grpc.backend.port` + property. + Options for this mode are: + name=<name> + The "chip" name, used to identify the "chip" instance. This + may be useful when several clients are connected, since each needs to use a + different name. + + In `controller` mode: + The <host>:<port> part is required. <host> may be the address of a local network + interface, or '_' to accept connections on all local network interfaces. + + Examples: + (empty string) --> connect to netsim on the port specified in the .ini file + localhost:8555 --> connect to netsim on localhost:8555 + name=bumble1 --> connect to netsim, using `bumble1` as the "chip" name. + localhost:8555,name=bumble1 --> connect to netsim on localhost:8555, using + `bumble1` as the "chip" name. + _:8877,mode=controller --> accept connections as a controller on any interface + on port 8877. + ''' + + # Parse the parameters + params = spec.split(',') if spec else [] + if params and ':' in params[0]: + # Explicit <host>:<port> + host, port = params[0].split(':') + params_offset = 1 + else: + host = None + port = 0 + params_offset = 0 + + options = {} + for param in params[params_offset:]: + if '=' not in param: + raise ValueError('invalid parameter, expected <name>=<value>') + option_name, option_value = param.split('=') + options[option_name] = option_value + + mode = options.get('mode', 'host') + if mode == 'host': + return await open_android_netsim_host_transport(host, port, options) + if mode == 'controller': + if host is None: + raise ValueError('<host>:<port> missing') + return await open_android_netsim_controller_transport(host, port) + + raise ValueError('invalid mode option') diff --git a/bumble/transport/common.py b/bumble/transport/common.py index d5c1ae9..05a1fb5 100644 --- a/bumble/transport/common.py +++ b/bumble/transport/common.py @@ -15,12 +15,16 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- +from __future__ import annotations +import contextlib import struct import asyncio import logging -from colors import color +from typing import ContextManager from .. import hci +from ..colors import color +from ..snoop import Snooper # ----------------------------------------------------------------------------- @@ -33,10 +37,10 @@ logger = logging.getLogger(__name__) # For each packet type, the info represents: # (length-size, length-offset, unpack-type) HCI_PACKET_INFO = { - hci.HCI_COMMAND_PACKET: (1, 2, 'B'), - hci.HCI_ACL_DATA_PACKET: (2, 2, 'H'), + hci.HCI_COMMAND_PACKET: (1, 2, 'B'), + hci.HCI_ACL_DATA_PACKET: (2, 2, 'H'), hci.HCI_SYNCHRONOUS_DATA_PACKET: (1, 2, 'B'), - hci.HCI_EVENT_PACKET: (1, 1, 'B') + hci.HCI_EVENT_PACKET: (1, 1, 'B'), } @@ -48,7 +52,7 @@ class PacketPump: def __init__(self, reader, sink): self.reader = reader - self.sink = sink + self.sink = sink async def run(self): while True: @@ -65,43 +69,51 @@ class PacketPump: # ----------------------------------------------------------------------------- class PacketParser: ''' - In-line parser that accepts data and emits 'on_packet' when a full packet has been parsed + In-line parser that accepts data and emits 'on_packet' when a full packet has been + parsed ''' - NEED_TYPE = 0 + + # pylint: disable=attribute-defined-outside-init + + NEED_TYPE = 0 NEED_LENGTH = 1 - NEED_BODY = 2 + NEED_BODY = 2 - def __init__(self, sink = None): + def __init__(self, sink=None): self.sink = sink self.extended_packet_info = {} self.reset() def reset(self): - self.state = PacketParser.NEED_TYPE + self.state = PacketParser.NEED_TYPE self.bytes_needed = 1 - self.packet = bytearray() - self.packet_info = None + self.packet = bytearray() + self.packet_info = None def feed_data(self, data): data_offset = 0 data_left = len(data) while data_left and self.bytes_needed: consumed = min(self.bytes_needed, data_left) - self.packet.extend(data[data_offset:data_offset + consumed]) - data_offset += consumed - data_left -= consumed + self.packet.extend(data[data_offset : data_offset + consumed]) + data_offset += consumed + data_left -= consumed self.bytes_needed -= consumed if self.bytes_needed == 0: if self.state == PacketParser.NEED_TYPE: packet_type = self.packet[0] - self.packet_info = HCI_PACKET_INFO.get(packet_type) or self.extended_packet_info.get(packet_type) + self.packet_info = HCI_PACKET_INFO.get( + packet_type + ) or self.extended_packet_info.get(packet_type) if self.packet_info is None: raise ValueError(f'invalid packet type {packet_type}') self.state = PacketParser.NEED_LENGTH self.bytes_needed = self.packet_info[0] + self.packet_info[1] elif self.state == PacketParser.NEED_LENGTH: - body_length = struct.unpack_from(self.packet_info[2], self.packet, 1 + self.packet_info[1])[0] + body_length = struct.unpack_from( + self.packet_info[2], self.packet, 1 + self.packet_info[1] + )[0] self.bytes_needed = body_length self.state = PacketParser.NEED_BODY @@ -111,7 +123,9 @@ class PacketParser: try: self.sink.on_packet(bytes(self.packet)) except Exception as error: - logger.warning(color(f'!!! Exception in on_packet: {error}', 'red')) + logger.warning( + color(f'!!! Exception in on_packet: {error}', 'red') + ) self.reset() def set_packet_sink(self, sink): @@ -187,6 +201,7 @@ class AsyncPipeSink: ''' Sink that forwards packets asynchronously to another sink ''' + def __init__(self, sink): self.sink = sink self.loop = asyncio.get_running_loop() @@ -202,7 +217,7 @@ class ParserSource: """ def __init__(self): - self.parser = PacketParser() + self.parser = PacketParser() self.terminated = asyncio.get_running_loop().create_future() def set_packet_sink(self, sink): @@ -235,9 +250,23 @@ class StreamPacketSink: # ----------------------------------------------------------------------------- class Transport: + """ + Base class for all transports. + + A Transport represents a source and a sink together. + An instance must be closed by calling close() when no longer used. Instances + implement the ContextManager protocol so that they may be used in a `async with` + statement. + An instance is iterable. The iterator yields, in order, its source and sink, so + that it may be used with a convenient call syntax like: + + async with create_transport() as (source, sink): + ... + """ + def __init__(self, source, sink): self.source = source - self.sink = sink + self.sink = sink async def __aenter__(self): return self @@ -248,7 +277,7 @@ class Transport: def __iter__(self): return iter((self.source, self.sink)) - async def close(self): + async def close(self) -> None: self.source.close() self.sink.close() @@ -258,7 +287,7 @@ class PumpedPacketSource(ParserSource): def __init__(self, receive): super().__init__() self.receive_function = receive - self.pump_task = None + self.pump_task = None def start(self): async def pump_packets(): @@ -270,11 +299,11 @@ class PumpedPacketSource(ParserSource): logger.debug('source pump task done') break except Exception as error: - logger.warn(f'exception while waiting for packet: {error}') + logger.warning(f'exception while waiting for packet: {error}') self.terminated.set_result(error) break - self.pump_task = asyncio.get_running_loop().create_task(pump_packets()) + self.pump_task = asyncio.create_task(pump_packets()) def close(self): if self.pump_task: @@ -285,8 +314,8 @@ class PumpedPacketSource(ParserSource): class PumpedPacketSink: def __init__(self, send): self.send_function = send - self.packet_queue = asyncio.Queue() - self.pump_task = None + self.packet_queue = asyncio.Queue() + self.pump_task = None def on_packet(self, packet): self.packet_queue.put_nowait(packet) @@ -301,10 +330,10 @@ class PumpedPacketSink: logger.debug('sink pump task done') break except Exception as error: - logger.warn(f'exception while sending packet: {error}') + logger.warning(f'exception while sending packet: {error}') break - self.pump_task = asyncio.get_running_loop().create_task(pump_packets()) + self.pump_task = asyncio.create_task(pump_packets()) def close(self): if self.pump_task: @@ -324,3 +353,60 @@ class PumpedTransport(Transport): async def close(self): await super().close() await self.close_function() + + +# ----------------------------------------------------------------------------- +class SnoopingTransport(Transport): + """Transport wrapper that snoops on packets to/from a wrapped transport.""" + + @staticmethod + def create_with( + transport: Transport, snooper: ContextManager[Snooper] + ) -> SnoopingTransport: + """ + Create an instance given a snooper that works as as context manager. + + The returned instance will exit the snooper context when it is closed. + """ + with contextlib.ExitStack() as exit_stack: + return SnoopingTransport( + transport, exit_stack.enter_context(snooper), exit_stack.pop_all().close + ) + raise RuntimeError('unexpected code path') # Satisfy the type checker + + class Source: + def __init__(self, source, snooper): + self.source = source + self.snooper = snooper + self.sink = None + + def set_packet_sink(self, sink): + self.sink = sink + self.source.set_packet_sink(self) + + def on_packet(self, packet): + self.snooper.snoop(packet, Snooper.Direction.CONTROLLER_TO_HOST) + if self.sink: + self.sink.on_packet(packet) + + class Sink: + def __init__(self, sink, snooper): + self.sink = sink + self.snooper = snooper + + def on_packet(self, packet): + self.snooper.snoop(packet, Snooper.Direction.HOST_TO_CONTROLLER) + if self.sink: + self.sink.on_packet(packet) + + def __init__(self, transport, snooper, close_snooper=None): + super().__init__( + self.Source(transport.source, snooper), self.Sink(transport.sink, snooper) + ) + self.transport = transport + self.close_snooper = close_snooper + + async def close(self): + await self.transport.close() + if self.close_snooper: + self.close_snooper() diff --git a/bumble/transport/emulated_bluetooth_pb2.py b/bumble/transport/emulated_bluetooth_pb2.py deleted file mode 100644 index 4da12d5..0000000 --- a/bumble/transport/emulated_bluetooth_pb2.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2021-2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: emulated_bluetooth.proto -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection -from google.protobuf import symbol_database as _symbol_database -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from . import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x65mulated_bluetooth.proto\x12\x1b\x61ndroid.emulation.bluetooth\x1a emulated_bluetooth_packets.proto\"\x19\n\x07RawData\x12\x0e\n\x06packet\x18\x01 \x01(\x0c\x32\xcb\x02\n\x18\x45mulatedBluetoothService\x12\x64\n\x12registerClassicPhy\x12$.android.emulation.bluetooth.RawData\x1a$.android.emulation.bluetooth.RawData(\x01\x30\x01\x12`\n\x0eregisterBlePhy\x12$.android.emulation.bluetooth.RawData\x1a$.android.emulation.bluetooth.RawData(\x01\x30\x01\x12g\n\x11registerHCIDevice\x12&.android.emulation.bluetooth.HCIPacket\x1a&.android.emulation.bluetooth.HCIPacket(\x01\x30\x01\x42\"\n\x1e\x63om.android.emulator.bluetoothP\x01\x62\x06proto3') - - - -_RAWDATA = DESCRIPTOR.message_types_by_name['RawData'] -RawData = _reflection.GeneratedProtocolMessageType('RawData', (_message.Message,), { - 'DESCRIPTOR' : _RAWDATA, - '__module__' : 'emulated_bluetooth_pb2' - # @@protoc_insertion_point(class_scope:android.emulation.bluetooth.RawData) - }) -_sym_db.RegisterMessage(RawData) - -_EMULATEDBLUETOOTHSERVICE = DESCRIPTOR.services_by_name['EmulatedBluetoothService'] -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\036com.android.emulator.bluetoothP\001' - _RAWDATA._serialized_start=91 - _RAWDATA._serialized_end=116 - _EMULATEDBLUETOOTHSERVICE._serialized_start=119 - _EMULATEDBLUETOOTHSERVICE._serialized_end=450 -# @@protoc_insertion_point(module_scope) diff --git a/bumble/transport/file.py b/bumble/transport/file.py index 841c62a..9c073d2 100644 --- a/bumble/transport/file.py +++ b/bumble/transport/file.py @@ -30,8 +30,9 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- async def open_file_transport(spec): ''' - Open a File transport (typically not for a real file, but for a PTY or other unix virtual files). - The parameter string is the path of the file to open + Open a File transport (typically not for a real file, but for a PTY or other unix + virtual files). + The parameter string is the path of the file to open. ''' # Open the file @@ -39,14 +40,12 @@ async def open_file_transport(spec): # Setup reading read_transport, packet_source = await asyncio.get_running_loop().connect_read_pipe( - lambda: StreamPacketSource(), - file + StreamPacketSource, file ) # Setup writing write_transport, _ = await asyncio.get_running_loop().connect_write_pipe( - lambda: asyncio.BaseProtocol(), - file + asyncio.BaseProtocol, file ) packet_sink = StreamPacketSink(write_transport) @@ -57,4 +56,3 @@ async def open_file_transport(spec): file.close() return FileTransport(packet_source, packet_sink) - diff --git a/bumble/transport/grpc_protobuf/__init__.py b/bumble/transport/grpc_protobuf/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bumble/transport/grpc_protobuf/__init__.py diff --git a/bumble/transport/grpc_protobuf/common_pb2.py b/bumble/transport/grpc_protobuf/common_pb2.py new file mode 100644 index 0000000..c54a2e0 --- /dev/null +++ b/bumble/transport/grpc_protobuf/common_pb2.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: common.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x63ommon.proto\x12\rnetsim.common*=\n\x08\x43hipKind\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\r\n\tBLUETOOTH\x10\x01\x12\x08\n\x04WIFI\x10\x02\x12\x07\n\x03UWB\x10\x03\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'common_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _CHIPKIND._serialized_start=31 + _CHIPKIND._serialized_end=92 +# @@protoc_insertion_point(module_scope) diff --git a/bumble/transport/grpc_protobuf/common_pb2.pyi b/bumble/transport/grpc_protobuf/common_pb2.pyi new file mode 100644 index 0000000..4cc934d --- /dev/null +++ b/bumble/transport/grpc_protobuf/common_pb2.pyi @@ -0,0 +1,12 @@ +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from typing import ClassVar as _ClassVar + +BLUETOOTH: ChipKind +DESCRIPTOR: _descriptor.FileDescriptor +UNSPECIFIED: ChipKind +UWB: ChipKind +WIFI: ChipKind + +class ChipKind(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] diff --git a/bumble/transport/grpc_protobuf/common_pb2_grpc.py b/bumble/transport/grpc_protobuf/common_pb2_grpc.py new file mode 100644 index 0000000..2daafff --- /dev/null +++ b/bumble/transport/grpc_protobuf/common_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/bumble/transport/grpc_protobuf/emulated_bluetooth_device_pb2.py b/bumble/transport/grpc_protobuf/emulated_bluetooth_device_pb2.py new file mode 100644 index 0000000..7380cb4 --- /dev/null +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_device_pb2.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: emulated_bluetooth_device.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import grpc_endpoint_description_pb2 as grpc__endpoint__description__pb2 +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x65mulated_bluetooth_device.proto\x12\x1b\x61ndroid.emulation.bluetooth\x1a\x1fgrpc_endpoint_description.proto\x1a\x1bgoogle/protobuf/empty.proto\"&\n\x12\x43\x61llbackIdentifier\x12\x10\n\x08identity\x18\x01 \x01(\t\"#\n\x10\x44\x65viceIdentifier\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\"\xf5\x02\n\x15\x43onnectionStateChange\x12K\n\x12\x63\x61llback_device_id\x18\x01 \x01(\x0b\x32/.android.emulation.bluetooth.CallbackIdentifier\x12\x42\n\x0b\x66rom_device\x18\x02 \x01(\x0b\x32-.android.emulation.bluetooth.DeviceIdentifier\x12U\n\tnew_state\x18\x03 \x01(\x0e\x32\x42.android.emulation.bluetooth.ConnectionStateChange.ConnectionState\"t\n\x0f\x43onnectionState\x12\x1e\n\x1a\x43ONNECTION_STATE_UNDEFINED\x10\x00\x12!\n\x1d\x43ONNECTION_STATE_DISCONNECTED\x10\x01\x12\x1e\n\x1a\x43ONNECTION_STATE_CONNECTED\x10\x02\"A\n\x04Uuid\x12\x0c\n\x02id\x18\x01 \x01(\rH\x00\x12\r\n\x03lsb\x18\x02 \x01(\x04H\x00\x12\x0b\n\x03msb\x18\x03 \x01(\x03\x42\x0f\n\rshort_or_long\"\xf3\x01\n\x1a\x43haracteristicValueRequest\x12K\n\x12\x63\x61llback_device_id\x18\x01 \x01(\x0b\x32/.android.emulation.bluetooth.CallbackIdentifier\x12\x42\n\x0b\x66rom_device\x18\x02 \x01(\x0b\x32-.android.emulation.bluetooth.DeviceIdentifier\x12\x36\n\x0b\x63\x61llback_id\x18\x03 \x01(\x0b\x32!.android.emulation.bluetooth.Uuid\x12\x0c\n\x04\x64\x61ta\x18\x04 \x01(\x0c\"\xdd\x01\n\x1b\x43haracteristicValueResponse\x12S\n\x06status\x18\x01 \x01(\x0e\x32\x43.android.emulation.bluetooth.CharacteristicValueResponse.GattStatus\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"[\n\nGattStatus\x12\x1b\n\x17GATT_STATUS_UNSPECIFIED\x10\x00\x12\x17\n\x13GATT_STATUS_SUCCESS\x10\x01\x12\x17\n\x13GATT_STATUS_FAILURE\x10\x02\"\xb3\x05\n\x12GattCharacteristic\x12/\n\x04uuid\x18\x01 \x01(\x0b\x32!.android.emulation.bluetooth.Uuid\x12\x12\n\nproperties\x18\x02 \x01(\r\x12\x13\n\x0bpermissions\x18\x03 \x01(\r\x12\x36\n\x0b\x63\x61llback_id\x18\x04 \x01(\x0b\x32!.android.emulation.bluetooth.Uuid\"\xea\x01\n\nProperties\x12\x18\n\x14PROPERTY_UNSPECIFIED\x10\x00\x12\x16\n\x12PROPERTY_BROADCAST\x10\x01\x12\x11\n\rPROPERTY_READ\x10\x02\x12\x1e\n\x1aPROPERTY_WRITE_NO_RESPONSE\x10\x04\x12\x12\n\x0ePROPERTY_WRITE\x10\x08\x12\x13\n\x0fPROPERTY_NOTIFY\x10\x10\x12\x15\n\x11PROPERTY_INDICATE\x10 \x12\x19\n\x15PROPERTY_SIGNED_WRITE\x10@\x12\x1c\n\x17PROPERTY_EXTENDED_PROPS\x10\x80\x01\"\x9d\x02\n\x0bPermissions\x12\x1a\n\x16PERMISSION_UNSPECIFIED\x10\x00\x12\x13\n\x0fPERMISSION_READ\x10\x01\x12\x1d\n\x19PERMISSION_READ_ENCRYPTED\x10\x02\x12\"\n\x1ePERMISSION_READ_ENCRYPTED_MITM\x10\x04\x12\x14\n\x10PERMISSION_WRITE\x10\x10\x12\x1e\n\x1aPERMISSION_WRITE_ENCRYPTED\x10 \x12#\n\x1fPERMISSION_WRITE_ENCRYPTED_MITM\x10@\x12\x1c\n\x17PERMISSION_WRITE_SIGNED\x10\x80\x01\x12!\n\x1cPERMISSION_WRITE_SIGNED_MITM\x10\x80\x02\"\xb7\x02\n\x0bGattService\x12/\n\x04uuid\x18\x01 \x01(\x0b\x32!.android.emulation.bluetooth.Uuid\x12J\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x34.android.emulation.bluetooth.GattService.ServiceType\x12H\n\x0f\x63haracteristics\x18\x03 \x03(\x0b\x32/.android.emulation.bluetooth.GattCharacteristic\"a\n\x0bServiceType\x12\x1c\n\x18SERVICE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14SERVICE_TYPE_PRIMARY\x10\x01\x12\x1a\n\x16SERVICE_TYPE_SECONDARY\x10\x02\"I\n\x0bGattProfile\x12:\n\x08services\x18\x01 \x03(\x0b\x32(.android.emulation.bluetooth.GattService\"\xf0\x03\n\rAdvertisement\x12\x13\n\x0b\x64\x65vice_name\x18\x01 \x01(\t\x12R\n\x0f\x63onnection_mode\x18\x02 \x01(\x0e\x32\x39.android.emulation.bluetooth.Advertisement.ConnectionMode\x12P\n\x0e\x64iscovery_mode\x18\x03 \x01(\x0e\x32\x38.android.emulation.bluetooth.Advertisement.DiscoveryMode\"\x94\x01\n\x0e\x43onnectionMode\x12\x1f\n\x1b\x43ONNECTION_MODE_UNSPECIFIED\x10\x00\x12#\n\x1f\x43ONNECTION_MODE_NON_CONNECTABLE\x10\x01\x12\x1c\n\x18\x43ONNECTION_MODE_DIRECTED\x10\x02\x12\x1e\n\x1a\x43ONNECTION_MODE_UNDIRECTED\x10\x03\"\x8c\x01\n\rDiscoveryMode\x12\x1e\n\x1a\x44ISCOVERY_MODE_UNSPECIFIED\x10\x00\x12#\n\x1f\x44ISCOVERY_MODE_NON_DISCOVERABLE\x10\x01\x12\x1a\n\x16\x44ISCOVERY_MODE_LIMITED\x10\x02\x12\x1a\n\x16\x44ISCOVERY_MODE_GENERAL\x10\x03\"\xc0\x01\n\nGattDevice\x12\x34\n\x08\x65ndpoint\x18\x01 \x01(\x0b\x32\".android.emulation.remote.Endpoint\x12\x41\n\radvertisement\x18\x02 \x01(\x0b\x32*.android.emulation.bluetooth.Advertisement\x12\x39\n\x07profile\x18\x03 \x01(\x0b\x32(.android.emulation.bluetooth.GattProfile2\xb9\x04\n\x11GattDeviceService\x12\x90\x01\n\x1bOnCharacteristicReadRequest\x12\x37.android.emulation.bluetooth.CharacteristicValueRequest\x1a\x38.android.emulation.bluetooth.CharacteristicValueResponse\x12\x91\x01\n\x1cOnCharacteristicWriteRequest\x12\x37.android.emulation.bluetooth.CharacteristicValueRequest\x1a\x38.android.emulation.bluetooth.CharacteristicValueResponse\x12\x95\x01\n\x1eOnCharacteristicObserveRequest\x12\x37.android.emulation.bluetooth.CharacteristicValueRequest\x1a\x38.android.emulation.bluetooth.CharacteristicValueResponse0\x01\x12\x65\n\x17OnConnectionStateChange\x12\x32.android.emulation.bluetooth.ConnectionStateChange\x1a\x16.google.protobuf.EmptyB\"\n\x1e\x63om.android.emulator.bluetoothP\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'emulated_bluetooth_device_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\036com.android.emulator.bluetoothP\001' + _CALLBACKIDENTIFIER._serialized_start=126 + _CALLBACKIDENTIFIER._serialized_end=164 + _DEVICEIDENTIFIER._serialized_start=166 + _DEVICEIDENTIFIER._serialized_end=201 + _CONNECTIONSTATECHANGE._serialized_start=204 + _CONNECTIONSTATECHANGE._serialized_end=577 + _CONNECTIONSTATECHANGE_CONNECTIONSTATE._serialized_start=461 + _CONNECTIONSTATECHANGE_CONNECTIONSTATE._serialized_end=577 + _UUID._serialized_start=579 + _UUID._serialized_end=644 + _CHARACTERISTICVALUEREQUEST._serialized_start=647 + _CHARACTERISTICVALUEREQUEST._serialized_end=890 + _CHARACTERISTICVALUERESPONSE._serialized_start=893 + _CHARACTERISTICVALUERESPONSE._serialized_end=1114 + _CHARACTERISTICVALUERESPONSE_GATTSTATUS._serialized_start=1023 + _CHARACTERISTICVALUERESPONSE_GATTSTATUS._serialized_end=1114 + _GATTCHARACTERISTIC._serialized_start=1117 + _GATTCHARACTERISTIC._serialized_end=1808 + _GATTCHARACTERISTIC_PROPERTIES._serialized_start=1286 + _GATTCHARACTERISTIC_PROPERTIES._serialized_end=1520 + _GATTCHARACTERISTIC_PERMISSIONS._serialized_start=1523 + _GATTCHARACTERISTIC_PERMISSIONS._serialized_end=1808 + _GATTSERVICE._serialized_start=1811 + _GATTSERVICE._serialized_end=2122 + _GATTSERVICE_SERVICETYPE._serialized_start=2025 + _GATTSERVICE_SERVICETYPE._serialized_end=2122 + _GATTPROFILE._serialized_start=2124 + _GATTPROFILE._serialized_end=2197 + _ADVERTISEMENT._serialized_start=2200 + _ADVERTISEMENT._serialized_end=2696 + _ADVERTISEMENT_CONNECTIONMODE._serialized_start=2405 + _ADVERTISEMENT_CONNECTIONMODE._serialized_end=2553 + _ADVERTISEMENT_DISCOVERYMODE._serialized_start=2556 + _ADVERTISEMENT_DISCOVERYMODE._serialized_end=2696 + _GATTDEVICE._serialized_start=2699 + _GATTDEVICE._serialized_end=2891 + _GATTDEVICESERVICE._serialized_start=2894 + _GATTDEVICESERVICE._serialized_end=3463 +# @@protoc_insertion_point(module_scope) diff --git a/bumble/transport/grpc_protobuf/emulated_bluetooth_device_pb2.pyi b/bumble/transport/grpc_protobuf/emulated_bluetooth_device_pb2.pyi new file mode 100644 index 0000000..c14501b --- /dev/null +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_device_pb2.pyi @@ -0,0 +1,158 @@ +from . import grpc_endpoint_description_pb2 as _grpc_endpoint_description_pb2 +from google.protobuf import empty_pb2 as _empty_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class Advertisement(_message.Message): + __slots__ = ["connection_mode", "device_name", "discovery_mode"] + class ConnectionMode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + class DiscoveryMode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + CONNECTION_MODE_DIRECTED: Advertisement.ConnectionMode + CONNECTION_MODE_FIELD_NUMBER: _ClassVar[int] + CONNECTION_MODE_NON_CONNECTABLE: Advertisement.ConnectionMode + CONNECTION_MODE_UNDIRECTED: Advertisement.ConnectionMode + CONNECTION_MODE_UNSPECIFIED: Advertisement.ConnectionMode + DEVICE_NAME_FIELD_NUMBER: _ClassVar[int] + DISCOVERY_MODE_FIELD_NUMBER: _ClassVar[int] + DISCOVERY_MODE_GENERAL: Advertisement.DiscoveryMode + DISCOVERY_MODE_LIMITED: Advertisement.DiscoveryMode + DISCOVERY_MODE_NON_DISCOVERABLE: Advertisement.DiscoveryMode + DISCOVERY_MODE_UNSPECIFIED: Advertisement.DiscoveryMode + connection_mode: Advertisement.ConnectionMode + device_name: str + discovery_mode: Advertisement.DiscoveryMode + def __init__(self, device_name: _Optional[str] = ..., connection_mode: _Optional[_Union[Advertisement.ConnectionMode, str]] = ..., discovery_mode: _Optional[_Union[Advertisement.DiscoveryMode, str]] = ...) -> None: ... + +class CallbackIdentifier(_message.Message): + __slots__ = ["identity"] + IDENTITY_FIELD_NUMBER: _ClassVar[int] + identity: str + def __init__(self, identity: _Optional[str] = ...) -> None: ... + +class CharacteristicValueRequest(_message.Message): + __slots__ = ["callback_device_id", "callback_id", "data", "from_device"] + CALLBACK_DEVICE_ID_FIELD_NUMBER: _ClassVar[int] + CALLBACK_ID_FIELD_NUMBER: _ClassVar[int] + DATA_FIELD_NUMBER: _ClassVar[int] + FROM_DEVICE_FIELD_NUMBER: _ClassVar[int] + callback_device_id: CallbackIdentifier + callback_id: Uuid + data: bytes + from_device: DeviceIdentifier + def __init__(self, callback_device_id: _Optional[_Union[CallbackIdentifier, _Mapping]] = ..., from_device: _Optional[_Union[DeviceIdentifier, _Mapping]] = ..., callback_id: _Optional[_Union[Uuid, _Mapping]] = ..., data: _Optional[bytes] = ...) -> None: ... + +class CharacteristicValueResponse(_message.Message): + __slots__ = ["data", "status"] + class GattStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + DATA_FIELD_NUMBER: _ClassVar[int] + GATT_STATUS_FAILURE: CharacteristicValueResponse.GattStatus + GATT_STATUS_SUCCESS: CharacteristicValueResponse.GattStatus + GATT_STATUS_UNSPECIFIED: CharacteristicValueResponse.GattStatus + STATUS_FIELD_NUMBER: _ClassVar[int] + data: bytes + status: CharacteristicValueResponse.GattStatus + def __init__(self, status: _Optional[_Union[CharacteristicValueResponse.GattStatus, str]] = ..., data: _Optional[bytes] = ...) -> None: ... + +class ConnectionStateChange(_message.Message): + __slots__ = ["callback_device_id", "from_device", "new_state"] + class ConnectionState(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + CALLBACK_DEVICE_ID_FIELD_NUMBER: _ClassVar[int] + CONNECTION_STATE_CONNECTED: ConnectionStateChange.ConnectionState + CONNECTION_STATE_DISCONNECTED: ConnectionStateChange.ConnectionState + CONNECTION_STATE_UNDEFINED: ConnectionStateChange.ConnectionState + FROM_DEVICE_FIELD_NUMBER: _ClassVar[int] + NEW_STATE_FIELD_NUMBER: _ClassVar[int] + callback_device_id: CallbackIdentifier + from_device: DeviceIdentifier + new_state: ConnectionStateChange.ConnectionState + def __init__(self, callback_device_id: _Optional[_Union[CallbackIdentifier, _Mapping]] = ..., from_device: _Optional[_Union[DeviceIdentifier, _Mapping]] = ..., new_state: _Optional[_Union[ConnectionStateChange.ConnectionState, str]] = ...) -> None: ... + +class DeviceIdentifier(_message.Message): + __slots__ = ["address"] + ADDRESS_FIELD_NUMBER: _ClassVar[int] + address: str + def __init__(self, address: _Optional[str] = ...) -> None: ... + +class GattCharacteristic(_message.Message): + __slots__ = ["callback_id", "permissions", "properties", "uuid"] + class Permissions(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + class Properties(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + CALLBACK_ID_FIELD_NUMBER: _ClassVar[int] + PERMISSIONS_FIELD_NUMBER: _ClassVar[int] + PERMISSION_READ: GattCharacteristic.Permissions + PERMISSION_READ_ENCRYPTED: GattCharacteristic.Permissions + PERMISSION_READ_ENCRYPTED_MITM: GattCharacteristic.Permissions + PERMISSION_UNSPECIFIED: GattCharacteristic.Permissions + PERMISSION_WRITE: GattCharacteristic.Permissions + PERMISSION_WRITE_ENCRYPTED: GattCharacteristic.Permissions + PERMISSION_WRITE_ENCRYPTED_MITM: GattCharacteristic.Permissions + PERMISSION_WRITE_SIGNED: GattCharacteristic.Permissions + PERMISSION_WRITE_SIGNED_MITM: GattCharacteristic.Permissions + PROPERTIES_FIELD_NUMBER: _ClassVar[int] + PROPERTY_BROADCAST: GattCharacteristic.Properties + PROPERTY_EXTENDED_PROPS: GattCharacteristic.Properties + PROPERTY_INDICATE: GattCharacteristic.Properties + PROPERTY_NOTIFY: GattCharacteristic.Properties + PROPERTY_READ: GattCharacteristic.Properties + PROPERTY_SIGNED_WRITE: GattCharacteristic.Properties + PROPERTY_UNSPECIFIED: GattCharacteristic.Properties + PROPERTY_WRITE: GattCharacteristic.Properties + PROPERTY_WRITE_NO_RESPONSE: GattCharacteristic.Properties + UUID_FIELD_NUMBER: _ClassVar[int] + callback_id: Uuid + permissions: int + properties: int + uuid: Uuid + def __init__(self, uuid: _Optional[_Union[Uuid, _Mapping]] = ..., properties: _Optional[int] = ..., permissions: _Optional[int] = ..., callback_id: _Optional[_Union[Uuid, _Mapping]] = ...) -> None: ... + +class GattDevice(_message.Message): + __slots__ = ["advertisement", "endpoint", "profile"] + ADVERTISEMENT_FIELD_NUMBER: _ClassVar[int] + ENDPOINT_FIELD_NUMBER: _ClassVar[int] + PROFILE_FIELD_NUMBER: _ClassVar[int] + advertisement: Advertisement + endpoint: _grpc_endpoint_description_pb2.Endpoint + profile: GattProfile + def __init__(self, endpoint: _Optional[_Union[_grpc_endpoint_description_pb2.Endpoint, _Mapping]] = ..., advertisement: _Optional[_Union[Advertisement, _Mapping]] = ..., profile: _Optional[_Union[GattProfile, _Mapping]] = ...) -> None: ... + +class GattProfile(_message.Message): + __slots__ = ["services"] + SERVICES_FIELD_NUMBER: _ClassVar[int] + services: _containers.RepeatedCompositeFieldContainer[GattService] + def __init__(self, services: _Optional[_Iterable[_Union[GattService, _Mapping]]] = ...) -> None: ... + +class GattService(_message.Message): + __slots__ = ["characteristics", "service_type", "uuid"] + class ServiceType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + CHARACTERISTICS_FIELD_NUMBER: _ClassVar[int] + SERVICE_TYPE_FIELD_NUMBER: _ClassVar[int] + SERVICE_TYPE_PRIMARY: GattService.ServiceType + SERVICE_TYPE_SECONDARY: GattService.ServiceType + SERVICE_TYPE_UNSPECIFIED: GattService.ServiceType + UUID_FIELD_NUMBER: _ClassVar[int] + characteristics: _containers.RepeatedCompositeFieldContainer[GattCharacteristic] + service_type: GattService.ServiceType + uuid: Uuid + def __init__(self, uuid: _Optional[_Union[Uuid, _Mapping]] = ..., service_type: _Optional[_Union[GattService.ServiceType, str]] = ..., characteristics: _Optional[_Iterable[_Union[GattCharacteristic, _Mapping]]] = ...) -> None: ... + +class Uuid(_message.Message): + __slots__ = ["id", "lsb", "msb"] + ID_FIELD_NUMBER: _ClassVar[int] + LSB_FIELD_NUMBER: _ClassVar[int] + MSB_FIELD_NUMBER: _ClassVar[int] + id: int + lsb: int + msb: int + def __init__(self, id: _Optional[int] = ..., lsb: _Optional[int] = ..., msb: _Optional[int] = ...) -> None: ... diff --git a/bumble/transport/grpc_protobuf/emulated_bluetooth_device_pb2_grpc.py b/bumble/transport/grpc_protobuf/emulated_bluetooth_device_pb2_grpc.py new file mode 100644 index 0000000..754627d --- /dev/null +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_device_pb2_grpc.py @@ -0,0 +1,193 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from . import emulated_bluetooth_device_pb2 as emulated__bluetooth__device__pb2 +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 + + +class GattDeviceServiceStub(object): + """You can provide your own GattDevice by implementing this service + and registering it with the android emulator. + + The device will appear as a real bluetooth device, and you will + receive callbacks when the bluetooth system wants to + read, write or observe a characteristic. + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.OnCharacteristicReadRequest = channel.unary_unary( + '/android.emulation.bluetooth.GattDeviceService/OnCharacteristicReadRequest', + request_serializer=emulated__bluetooth__device__pb2.CharacteristicValueRequest.SerializeToString, + response_deserializer=emulated__bluetooth__device__pb2.CharacteristicValueResponse.FromString, + ) + self.OnCharacteristicWriteRequest = channel.unary_unary( + '/android.emulation.bluetooth.GattDeviceService/OnCharacteristicWriteRequest', + request_serializer=emulated__bluetooth__device__pb2.CharacteristicValueRequest.SerializeToString, + response_deserializer=emulated__bluetooth__device__pb2.CharacteristicValueResponse.FromString, + ) + self.OnCharacteristicObserveRequest = channel.unary_stream( + '/android.emulation.bluetooth.GattDeviceService/OnCharacteristicObserveRequest', + request_serializer=emulated__bluetooth__device__pb2.CharacteristicValueRequest.SerializeToString, + response_deserializer=emulated__bluetooth__device__pb2.CharacteristicValueResponse.FromString, + ) + self.OnConnectionStateChange = channel.unary_unary( + '/android.emulation.bluetooth.GattDeviceService/OnConnectionStateChange', + request_serializer=emulated__bluetooth__device__pb2.ConnectionStateChange.SerializeToString, + response_deserializer=google_dot_protobuf_dot_empty__pb2.Empty.FromString, + ) + + +class GattDeviceServiceServicer(object): + """You can provide your own GattDevice by implementing this service + and registering it with the android emulator. + + The device will appear as a real bluetooth device, and you will + receive callbacks when the bluetooth system wants to + read, write or observe a characteristic. + """ + + def OnCharacteristicReadRequest(self, request, context): + """A remote client has requested to read a local characteristic. + + Return the current observed value. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def OnCharacteristicWriteRequest(self, request, context): + """A remote client has requested to write to a local characteristic. + + Return the current observed value. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def OnCharacteristicObserveRequest(self, request, context): + """Listens for notifications from the emulated device, the device should + write to the stream with a response when a change has occurred. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def OnConnectionStateChange(self, request, context): + """A remote device has been connected or disconnected. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_GattDeviceServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'OnCharacteristicReadRequest': grpc.unary_unary_rpc_method_handler( + servicer.OnCharacteristicReadRequest, + request_deserializer=emulated__bluetooth__device__pb2.CharacteristicValueRequest.FromString, + response_serializer=emulated__bluetooth__device__pb2.CharacteristicValueResponse.SerializeToString, + ), + 'OnCharacteristicWriteRequest': grpc.unary_unary_rpc_method_handler( + servicer.OnCharacteristicWriteRequest, + request_deserializer=emulated__bluetooth__device__pb2.CharacteristicValueRequest.FromString, + response_serializer=emulated__bluetooth__device__pb2.CharacteristicValueResponse.SerializeToString, + ), + 'OnCharacteristicObserveRequest': grpc.unary_stream_rpc_method_handler( + servicer.OnCharacteristicObserveRequest, + request_deserializer=emulated__bluetooth__device__pb2.CharacteristicValueRequest.FromString, + response_serializer=emulated__bluetooth__device__pb2.CharacteristicValueResponse.SerializeToString, + ), + 'OnConnectionStateChange': grpc.unary_unary_rpc_method_handler( + servicer.OnConnectionStateChange, + request_deserializer=emulated__bluetooth__device__pb2.ConnectionStateChange.FromString, + response_serializer=google_dot_protobuf_dot_empty__pb2.Empty.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'android.emulation.bluetooth.GattDeviceService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class GattDeviceService(object): + """You can provide your own GattDevice by implementing this service + and registering it with the android emulator. + + The device will appear as a real bluetooth device, and you will + receive callbacks when the bluetooth system wants to + read, write or observe a characteristic. + """ + + @staticmethod + def OnCharacteristicReadRequest(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/android.emulation.bluetooth.GattDeviceService/OnCharacteristicReadRequest', + emulated__bluetooth__device__pb2.CharacteristicValueRequest.SerializeToString, + emulated__bluetooth__device__pb2.CharacteristicValueResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def OnCharacteristicWriteRequest(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/android.emulation.bluetooth.GattDeviceService/OnCharacteristicWriteRequest', + emulated__bluetooth__device__pb2.CharacteristicValueRequest.SerializeToString, + emulated__bluetooth__device__pb2.CharacteristicValueResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def OnCharacteristicObserveRequest(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_stream(request, target, '/android.emulation.bluetooth.GattDeviceService/OnCharacteristicObserveRequest', + emulated__bluetooth__device__pb2.CharacteristicValueRequest.SerializeToString, + emulated__bluetooth__device__pb2.CharacteristicValueResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def OnConnectionStateChange(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/android.emulation.bluetooth.GattDeviceService/OnConnectionStateChange', + emulated__bluetooth__device__pb2.ConnectionStateChange.SerializeToString, + google_dot_protobuf_dot_empty__pb2.Empty.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/bumble/transport/emulated_bluetooth_packets_pb2.py b/bumble/transport/grpc_protobuf/emulated_bluetooth_packets_pb2.py index 9d3591d..8544f95 100644 --- a/bumble/transport/emulated_bluetooth_packets_pb2.py +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_packets_pb2.py @@ -1,25 +1,10 @@ -# Copyright 2021-2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: emulated_bluetooth_packets.proto """Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) @@ -30,17 +15,8 @@ _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n emulated_bluetooth_packets.proto\x12\x1b\x61ndroid.emulation.bluetooth\"\xfb\x01\n\tHCIPacket\x12?\n\x04type\x18\x01 \x01(\x0e\x32\x31.android.emulation.bluetooth.HCIPacket.PacketType\x12\x0e\n\x06packet\x18\x02 \x01(\x0c\"\x9c\x01\n\nPacketType\x12\x1b\n\x17PACKET_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17PACKET_TYPE_HCI_COMMAND\x10\x01\x12\x13\n\x0fPACKET_TYPE_ACL\x10\x02\x12\x13\n\x0fPACKET_TYPE_SCO\x10\x03\x12\x15\n\x11PACKET_TYPE_EVENT\x10\x04\x12\x13\n\x0fPACKET_TYPE_ISO\x10\x05\x42J\n\x1f\x63om.android.emulation.bluetoothP\x01\xf8\x01\x01\xa2\x02\x03\x41\x45\x42\xaa\x02\x1b\x41ndroid.Emulation.Bluetoothb\x06proto3') - - -_HCIPACKET = DESCRIPTOR.message_types_by_name['HCIPacket'] -_HCIPACKET_PACKETTYPE = _HCIPACKET.enum_types_by_name['PacketType'] -HCIPacket = _reflection.GeneratedProtocolMessageType('HCIPacket', (_message.Message,), { - 'DESCRIPTOR' : _HCIPACKET, - '__module__' : 'emulated_bluetooth_packets_pb2' - # @@protoc_insertion_point(class_scope:android.emulation.bluetooth.HCIPacket) - }) -_sym_db.RegisterMessage(HCIPacket) - +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'emulated_bluetooth_packets_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None diff --git a/bumble/transport/grpc_protobuf/emulated_bluetooth_packets_pb2.pyi b/bumble/transport/grpc_protobuf/emulated_bluetooth_packets_pb2.pyi new file mode 100644 index 0000000..80cdee1 --- /dev/null +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_packets_pb2.pyi @@ -0,0 +1,22 @@ +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HCIPacket(_message.Message): + __slots__ = ["packet", "type"] + class PacketType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + PACKET_FIELD_NUMBER: _ClassVar[int] + PACKET_TYPE_ACL: HCIPacket.PacketType + PACKET_TYPE_EVENT: HCIPacket.PacketType + PACKET_TYPE_HCI_COMMAND: HCIPacket.PacketType + PACKET_TYPE_ISO: HCIPacket.PacketType + PACKET_TYPE_SCO: HCIPacket.PacketType + PACKET_TYPE_UNSPECIFIED: HCIPacket.PacketType + TYPE_FIELD_NUMBER: _ClassVar[int] + packet: bytes + type: HCIPacket.PacketType + def __init__(self, type: _Optional[_Union[HCIPacket.PacketType, str]] = ..., packet: _Optional[bytes] = ...) -> None: ... diff --git a/bumble/transport/grpc_protobuf/emulated_bluetooth_packets_pb2_grpc.py b/bumble/transport/grpc_protobuf/emulated_bluetooth_packets_pb2_grpc.py new file mode 100644 index 0000000..2daafff --- /dev/null +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_packets_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/bumble/transport/grpc_protobuf/emulated_bluetooth_pb2.py b/bumble/transport/grpc_protobuf/emulated_bluetooth_pb2.py new file mode 100644 index 0000000..054b250 --- /dev/null +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_pb2.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: emulated_bluetooth.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2 +from . import emulated_bluetooth_device_pb2 as emulated__bluetooth__device__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x65mulated_bluetooth.proto\x12\x1b\x61ndroid.emulation.bluetooth\x1a emulated_bluetooth_packets.proto\x1a\x1f\x65mulated_bluetooth_device.proto\"\x19\n\x07RawData\x12\x0e\n\x06packet\x18\x01 \x01(\x0c\"a\n\x12RegistrationStatus\x12K\n\x12\x63\x61llback_device_id\x18\x01 \x01(\x0b\x32/.android.emulation.bluetooth.CallbackIdentifier2\xbb\x03\n\x18\x45mulatedBluetoothService\x12\x64\n\x12registerClassicPhy\x12$.android.emulation.bluetooth.RawData\x1a$.android.emulation.bluetooth.RawData(\x01\x30\x01\x12`\n\x0eregisterBlePhy\x12$.android.emulation.bluetooth.RawData\x1a$.android.emulation.bluetooth.RawData(\x01\x30\x01\x12g\n\x11registerHCIDevice\x12&.android.emulation.bluetooth.HCIPacket\x1a&.android.emulation.bluetooth.HCIPacket(\x01\x30\x01\x12n\n\x12registerGattDevice\x12\'.android.emulation.bluetooth.GattDevice\x1a/.android.emulation.bluetooth.RegistrationStatusB\"\n\x1e\x63om.android.emulator.bluetoothP\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'emulated_bluetooth_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\036com.android.emulator.bluetoothP\001' + _RAWDATA._serialized_start=124 + _RAWDATA._serialized_end=149 + _REGISTRATIONSTATUS._serialized_start=151 + _REGISTRATIONSTATUS._serialized_end=248 + _EMULATEDBLUETOOTHSERVICE._serialized_start=251 + _EMULATEDBLUETOOTHSERVICE._serialized_end=694 +# @@protoc_insertion_point(module_scope) diff --git a/bumble/transport/grpc_protobuf/emulated_bluetooth_pb2.pyi b/bumble/transport/grpc_protobuf/emulated_bluetooth_pb2.pyi new file mode 100644 index 0000000..9f54383 --- /dev/null +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_pb2.pyi @@ -0,0 +1,19 @@ +from . import emulated_bluetooth_packets_pb2 as _emulated_bluetooth_packets_pb2 +from . import emulated_bluetooth_device_pb2 as _emulated_bluetooth_device_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class RawData(_message.Message): + __slots__ = ["packet"] + PACKET_FIELD_NUMBER: _ClassVar[int] + packet: bytes + def __init__(self, packet: _Optional[bytes] = ...) -> None: ... + +class RegistrationStatus(_message.Message): + __slots__ = ["callback_device_id"] + CALLBACK_DEVICE_ID_FIELD_NUMBER: _ClassVar[int] + callback_device_id: _emulated_bluetooth_device_pb2.CallbackIdentifier + def __init__(self, callback_device_id: _Optional[_Union[_emulated_bluetooth_device_pb2.CallbackIdentifier, _Mapping]] = ...) -> None: ... diff --git a/bumble/transport/emulated_bluetooth_pb2_grpc.py b/bumble/transport/grpc_protobuf/emulated_bluetooth_pb2_grpc.py index cc0ce37..9ad3bb9 100644 --- a/bumble/transport/emulated_bluetooth_pb2_grpc.py +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_pb2_grpc.py @@ -1,21 +1,8 @@ -# Copyright 2021-2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +from . import emulated_bluetooth_device_pb2 as emulated__bluetooth__device__pb2 from . import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2 from . import emulated_bluetooth_pb2 as emulated__bluetooth__pb2 @@ -53,6 +40,11 @@ class EmulatedBluetoothServiceStub(object): request_serializer=emulated__bluetooth__packets__pb2.HCIPacket.SerializeToString, response_deserializer=emulated__bluetooth__packets__pb2.HCIPacket.FromString, ) + self.registerGattDevice = channel.unary_unary( + '/android.emulation.bluetooth.EmulatedBluetoothService/registerGattDevice', + request_serializer=emulated__bluetooth__device__pb2.GattDevice.SerializeToString, + response_deserializer=emulated__bluetooth__pb2.RegistrationStatus.FromString, + ) class EmulatedBluetoothServiceServicer(object): @@ -118,6 +110,22 @@ class EmulatedBluetoothServiceServicer(object): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def registerGattDevice(self, request, context): + """Registers an emulated bluetooth device. The emulator will reach out to + the emulated device to read/write and subscribe to properties. + + The following gRPC error codes can be returned: + - FAILED_PRECONDITION (code 9): + - root canal is not available on this device + - unable to reach the endpoint for the GattDevice + - INTERNAL (code 13) if there was an internal emulator failure. + + The device will not be discoverable in case of an error. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_EmulatedBluetoothServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -136,6 +144,11 @@ def add_EmulatedBluetoothServiceServicer_to_server(servicer, server): request_deserializer=emulated__bluetooth__packets__pb2.HCIPacket.FromString, response_serializer=emulated__bluetooth__packets__pb2.HCIPacket.SerializeToString, ), + 'registerGattDevice': grpc.unary_unary_rpc_method_handler( + servicer.registerGattDevice, + request_deserializer=emulated__bluetooth__device__pb2.GattDevice.FromString, + response_serializer=emulated__bluetooth__pb2.RegistrationStatus.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'android.emulation.bluetooth.EmulatedBluetoothService', rpc_method_handlers) @@ -205,3 +218,20 @@ class EmulatedBluetoothService(object): emulated__bluetooth__packets__pb2.HCIPacket.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def registerGattDevice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/android.emulation.bluetooth.EmulatedBluetoothService/registerGattDevice', + emulated__bluetooth__device__pb2.GattDevice.SerializeToString, + emulated__bluetooth__pb2.RegistrationStatus.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/bumble/transport/emulated_bluetooth_vhci_pb2.py b/bumble/transport/grpc_protobuf/emulated_bluetooth_vhci_pb2.py index a638439..30a2cb6 100644 --- a/bumble/transport/emulated_bluetooth_vhci_pb2.py +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_vhci_pb2.py @@ -1,39 +1,23 @@ -# Copyright 2021-2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: emulated_bluetooth_vhci.proto """Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() -import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2 +from . import emulated_bluetooth_packets_pb2 as emulated__bluetooth__packets__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x65mulated_bluetooth_vhci.proto\x12\x1b\x61ndroid.emulation.bluetooth\x1a emulated_bluetooth_packets.proto2y\n\x15VhciForwardingService\x12`\n\nattachVhci\x12&.android.emulation.bluetooth.HCIPacket\x1a&.android.emulation.bluetooth.HCIPacket(\x01\x30\x01\x42J\n\x1f\x63om.android.emulation.bluetoothP\x01\xf8\x01\x01\xa2\x02\x03\x41\x45\x42\xaa\x02\x1b\x41ndroid.Emulation.Bluetoothb\x06proto3') - - -_VHCIFORWARDINGSERVICE = DESCRIPTOR.services_by_name['VhciForwardingService'] +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'emulated_bluetooth_vhci_pb2', globals()) if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._options = None diff --git a/bumble/transport/grpc_protobuf/emulated_bluetooth_vhci_pb2.pyi b/bumble/transport/grpc_protobuf/emulated_bluetooth_vhci_pb2.pyi new file mode 100644 index 0000000..3311c9b --- /dev/null +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_vhci_pb2.pyi @@ -0,0 +1,5 @@ +import emulated_bluetooth_packets_pb2 as _emulated_bluetooth_packets_pb2 +from google.protobuf import descriptor as _descriptor +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor diff --git a/bumble/transport/emulated_bluetooth_vhci_pb2_grpc.py b/bumble/transport/grpc_protobuf/emulated_bluetooth_vhci_pb2_grpc.py index 94140d7..031f669 100644 --- a/bumble/transport/emulated_bluetooth_vhci_pb2_grpc.py +++ b/bumble/transport/grpc_protobuf/emulated_bluetooth_vhci_pb2_grpc.py @@ -1,17 +1,3 @@ -# Copyright 2021-2022 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc diff --git a/bumble/transport/grpc_protobuf/grpc_endpoint_description_pb2.py b/bumble/transport/grpc_protobuf/grpc_endpoint_description_pb2.py new file mode 100644 index 0000000..00d404c --- /dev/null +++ b/bumble/transport/grpc_protobuf/grpc_endpoint_description_pb2.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: grpc_endpoint_description.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fgrpc_endpoint_description.proto\x12\x18\x61ndroid.emulation.remote\"V\n\x0b\x43redentials\x12\x16\n\x0epem_root_certs\x18\x01 \x01(\t\x12\x17\n\x0fpem_private_key\x18\x02 \x01(\t\x12\x16\n\x0epem_cert_chain\x18\x03 \x01(\t\"$\n\x06Header\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"\x96\x01\n\x08\x45ndpoint\x12\x0e\n\x06target\x18\x01 \x01(\t\x12>\n\x0ftls_credentials\x18\x02 \x01(\x0b\x32%.android.emulation.remote.Credentials\x12:\n\x10required_headers\x18\x03 \x03(\x0b\x32 .android.emulation.remote.HeaderB \n\x1c\x63om.android.emulation.remoteP\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'grpc_endpoint_description_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\034com.android.emulation.remoteP\001' + _CREDENTIALS._serialized_start=61 + _CREDENTIALS._serialized_end=147 + _HEADER._serialized_start=149 + _HEADER._serialized_end=185 + _ENDPOINT._serialized_start=188 + _ENDPOINT._serialized_end=338 +# @@protoc_insertion_point(module_scope) diff --git a/bumble/transport/grpc_protobuf/grpc_endpoint_description_pb2.pyi b/bumble/transport/grpc_protobuf/grpc_endpoint_description_pb2.pyi new file mode 100644 index 0000000..3b81215 --- /dev/null +++ b/bumble/transport/grpc_protobuf/grpc_endpoint_description_pb2.pyi @@ -0,0 +1,34 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class Credentials(_message.Message): + __slots__ = ["pem_cert_chain", "pem_private_key", "pem_root_certs"] + PEM_CERT_CHAIN_FIELD_NUMBER: _ClassVar[int] + PEM_PRIVATE_KEY_FIELD_NUMBER: _ClassVar[int] + PEM_ROOT_CERTS_FIELD_NUMBER: _ClassVar[int] + pem_cert_chain: str + pem_private_key: str + pem_root_certs: str + def __init__(self, pem_root_certs: _Optional[str] = ..., pem_private_key: _Optional[str] = ..., pem_cert_chain: _Optional[str] = ...) -> None: ... + +class Endpoint(_message.Message): + __slots__ = ["required_headers", "target", "tls_credentials"] + REQUIRED_HEADERS_FIELD_NUMBER: _ClassVar[int] + TARGET_FIELD_NUMBER: _ClassVar[int] + TLS_CREDENTIALS_FIELD_NUMBER: _ClassVar[int] + required_headers: _containers.RepeatedCompositeFieldContainer[Header] + target: str + tls_credentials: Credentials + def __init__(self, target: _Optional[str] = ..., tls_credentials: _Optional[_Union[Credentials, _Mapping]] = ..., required_headers: _Optional[_Iterable[_Union[Header, _Mapping]]] = ...) -> None: ... + +class Header(_message.Message): + __slots__ = ["key", "value"] + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... diff --git a/bumble/transport/grpc_protobuf/grpc_endpoint_description_pb2_grpc.py b/bumble/transport/grpc_protobuf/grpc_endpoint_description_pb2_grpc.py new file mode 100644 index 0000000..2daafff --- /dev/null +++ b/bumble/transport/grpc_protobuf/grpc_endpoint_description_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/bumble/transport/grpc_protobuf/hci_packet_pb2.py b/bumble/transport/grpc_protobuf/hci_packet_pb2.py new file mode 100644 index 0000000..ef014c4 --- /dev/null +++ b/bumble/transport/grpc_protobuf/hci_packet_pb2.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: hci_packet.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10hci_packet.proto\x12\rnetsim.packet\"\xb2\x01\n\tHCIPacket\x12\x38\n\x0bpacket_type\x18\x01 \x01(\x0e\x32#.netsim.packet.HCIPacket.PacketType\x12\x0e\n\x06packet\x18\x02 \x01(\x0c\"[\n\nPacketType\x12\x1a\n\x16HCI_PACKET_UNSPECIFIED\x10\x00\x12\x0b\n\x07\x43OMMAND\x10\x01\x12\x07\n\x03\x41\x43L\x10\x02\x12\x07\n\x03SCO\x10\x03\x12\t\n\x05\x45VENT\x10\x04\x12\x07\n\x03ISO\x10\x05\x42J\n\x1f\x63om.android.emulation.bluetoothP\x01\xf8\x01\x01\xa2\x02\x03\x41\x45\x42\xaa\x02\x1b\x41ndroid.Emulation.Bluetoothb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'hci_packet_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\037com.android.emulation.bluetoothP\001\370\001\001\242\002\003AEB\252\002\033Android.Emulation.Bluetooth' + _HCIPACKET._serialized_start=36 + _HCIPACKET._serialized_end=214 + _HCIPACKET_PACKETTYPE._serialized_start=123 + _HCIPACKET_PACKETTYPE._serialized_end=214 +# @@protoc_insertion_point(module_scope) diff --git a/bumble/transport/grpc_protobuf/hci_packet_pb2.pyi b/bumble/transport/grpc_protobuf/hci_packet_pb2.pyi new file mode 100644 index 0000000..04bb972 --- /dev/null +++ b/bumble/transport/grpc_protobuf/hci_packet_pb2.pyi @@ -0,0 +1,22 @@ +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class HCIPacket(_message.Message): + __slots__ = ["packet", "packet_type"] + class PacketType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = [] + ACL: HCIPacket.PacketType + COMMAND: HCIPacket.PacketType + EVENT: HCIPacket.PacketType + HCI_PACKET_UNSPECIFIED: HCIPacket.PacketType + ISO: HCIPacket.PacketType + PACKET_FIELD_NUMBER: _ClassVar[int] + PACKET_TYPE_FIELD_NUMBER: _ClassVar[int] + SCO: HCIPacket.PacketType + packet: bytes + packet_type: HCIPacket.PacketType + def __init__(self, packet_type: _Optional[_Union[HCIPacket.PacketType, str]] = ..., packet: _Optional[bytes] = ...) -> None: ... diff --git a/bumble/transport/grpc_protobuf/hci_packet_pb2_grpc.py b/bumble/transport/grpc_protobuf/hci_packet_pb2_grpc.py new file mode 100644 index 0000000..2daafff --- /dev/null +++ b/bumble/transport/grpc_protobuf/hci_packet_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/bumble/transport/grpc_protobuf/packet_streamer_pb2.py b/bumble/transport/grpc_protobuf/packet_streamer_pb2.py new file mode 100644 index 0000000..ea07940 --- /dev/null +++ b/bumble/transport/grpc_protobuf/packet_streamer_pb2.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: packet_streamer.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import hci_packet_pb2 as hci__packet__pb2 +from . import startup_pb2 as startup__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15packet_streamer.proto\x12\rnetsim.packet\x1a\x10hci_packet.proto\x1a\rstartup.proto\"\x93\x01\n\rPacketRequest\x12\x30\n\x0cinitial_info\x18\x01 \x01(\x0b\x32\x18.netsim.startup.ChipInfoH\x00\x12.\n\nhci_packet\x18\x02 \x01(\x0b\x32\x18.netsim.packet.HCIPacketH\x00\x12\x10\n\x06packet\x18\x03 \x01(\x0cH\x00\x42\x0e\n\x0crequest_type\"t\n\x0ePacketResponse\x12\x0f\n\x05\x65rror\x18\x01 \x01(\tH\x00\x12.\n\nhci_packet\x18\x02 \x01(\x0b\x32\x18.netsim.packet.HCIPacketH\x00\x12\x10\n\x06packet\x18\x03 \x01(\x0cH\x00\x42\x0f\n\rresponse_type2b\n\x0ePacketStreamer\x12P\n\rStreamPackets\x12\x1c.netsim.packet.PacketRequest\x1a\x1d.netsim.packet.PacketResponse(\x01\x30\x01\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'packet_streamer_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _PACKETREQUEST._serialized_start=74 + _PACKETREQUEST._serialized_end=221 + _PACKETRESPONSE._serialized_start=223 + _PACKETRESPONSE._serialized_end=339 + _PACKETSTREAMER._serialized_start=341 + _PACKETSTREAMER._serialized_end=439 +# @@protoc_insertion_point(module_scope) diff --git a/bumble/transport/grpc_protobuf/packet_streamer_pb2.pyi b/bumble/transport/grpc_protobuf/packet_streamer_pb2.pyi new file mode 100644 index 0000000..d867613 --- /dev/null +++ b/bumble/transport/grpc_protobuf/packet_streamer_pb2.pyi @@ -0,0 +1,27 @@ +from . import hci_packet_pb2 as _hci_packet_pb2 +from . import startup_pb2 as _startup_pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class PacketRequest(_message.Message): + __slots__ = ["hci_packet", "initial_info", "packet"] + HCI_PACKET_FIELD_NUMBER: _ClassVar[int] + INITIAL_INFO_FIELD_NUMBER: _ClassVar[int] + PACKET_FIELD_NUMBER: _ClassVar[int] + hci_packet: _hci_packet_pb2.HCIPacket + initial_info: _startup_pb2.ChipInfo + packet: bytes + def __init__(self, initial_info: _Optional[_Union[_startup_pb2.ChipInfo, _Mapping]] = ..., hci_packet: _Optional[_Union[_hci_packet_pb2.HCIPacket, _Mapping]] = ..., packet: _Optional[bytes] = ...) -> None: ... + +class PacketResponse(_message.Message): + __slots__ = ["error", "hci_packet", "packet"] + ERROR_FIELD_NUMBER: _ClassVar[int] + HCI_PACKET_FIELD_NUMBER: _ClassVar[int] + PACKET_FIELD_NUMBER: _ClassVar[int] + error: str + hci_packet: _hci_packet_pb2.HCIPacket + packet: bytes + def __init__(self, error: _Optional[str] = ..., hci_packet: _Optional[_Union[_hci_packet_pb2.HCIPacket, _Mapping]] = ..., packet: _Optional[bytes] = ...) -> None: ... diff --git a/bumble/transport/grpc_protobuf/packet_streamer_pb2_grpc.py b/bumble/transport/grpc_protobuf/packet_streamer_pb2_grpc.py new file mode 100644 index 0000000..45ab653 --- /dev/null +++ b/bumble/transport/grpc_protobuf/packet_streamer_pb2_grpc.py @@ -0,0 +1,109 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from . import packet_streamer_pb2 as packet__streamer__pb2 + + +class PacketStreamerStub(object): + """* + This is the packet service for the network simulator. + + Android Virtual Devices (AVDs) and accessory devices use this service to + connect to the network simulator and pass packets back and forth. + + AVDs running in a guest VM are built with virtual controllers for each radio + chip. These controllers route chip requests to host emulators (qemu and + crosvm) using virtio and from there they are forwarded to this gRpc service. + + This setup provides a transparent radio environment across AVDs and + accessories because the network simulator contains libraries to emulate + Bluetooth, 80211MAC, UWB, and Rtt chips. + + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.StreamPackets = channel.stream_stream( + '/netsim.packet.PacketStreamer/StreamPackets', + request_serializer=packet__streamer__pb2.PacketRequest.SerializeToString, + response_deserializer=packet__streamer__pb2.PacketResponse.FromString, + ) + + +class PacketStreamerServicer(object): + """* + This is the packet service for the network simulator. + + Android Virtual Devices (AVDs) and accessory devices use this service to + connect to the network simulator and pass packets back and forth. + + AVDs running in a guest VM are built with virtual controllers for each radio + chip. These controllers route chip requests to host emulators (qemu and + crosvm) using virtio and from there they are forwarded to this gRpc service. + + This setup provides a transparent radio environment across AVDs and + accessories because the network simulator contains libraries to emulate + Bluetooth, 80211MAC, UWB, and Rtt chips. + + """ + + def StreamPackets(self, request_iterator, context): + """Attach a virtual radio controller to the network simulation. + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_PacketStreamerServicer_to_server(servicer, server): + rpc_method_handlers = { + 'StreamPackets': grpc.stream_stream_rpc_method_handler( + servicer.StreamPackets, + request_deserializer=packet__streamer__pb2.PacketRequest.FromString, + response_serializer=packet__streamer__pb2.PacketResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'netsim.packet.PacketStreamer', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class PacketStreamer(object): + """* + This is the packet service for the network simulator. + + Android Virtual Devices (AVDs) and accessory devices use this service to + connect to the network simulator and pass packets back and forth. + + AVDs running in a guest VM are built with virtual controllers for each radio + chip. These controllers route chip requests to host emulators (qemu and + crosvm) using virtio and from there they are forwarded to this gRpc service. + + This setup provides a transparent radio environment across AVDs and + accessories because the network simulator contains libraries to emulate + Bluetooth, 80211MAC, UWB, and Rtt chips. + + """ + + @staticmethod + def StreamPackets(request_iterator, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.stream_stream(request_iterator, target, '/netsim.packet.PacketStreamer/StreamPackets', + packet__streamer__pb2.PacketRequest.SerializeToString, + packet__streamer__pb2.PacketResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/bumble/transport/grpc_protobuf/startup_pb2.py b/bumble/transport/grpc_protobuf/startup_pb2.py new file mode 100644 index 0000000..532ac0e --- /dev/null +++ b/bumble/transport/grpc_protobuf/startup_pb2.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: startup.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import common_pb2 as common__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\rstartup.proto\x12\x0enetsim.startup\x1a\x0c\x63ommon.proto\"\x7f\n\x0bStartupInfo\x12\x33\n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\".netsim.startup.StartupInfo.Device\x1a;\n\x06\x44\x65vice\x12\x0c\n\x04name\x18\x01 \x01(\t\x12#\n\x05\x63hips\x18\x02 \x03(\x0b\x32\x14.netsim.startup.Chip\"<\n\x08\x43hipInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\"\n\x04\x63hip\x18\x02 \x01(\x0b\x32\x14.netsim.startup.Chip\"\x96\x01\n\x04\x43hip\x12%\n\x04kind\x18\x01 \x01(\x0e\x32\x17.netsim.common.ChipKind\x12\n\n\x02id\x18\x02 \x01(\t\x12\x14\n\x0cmanufacturer\x18\x03 \x01(\t\x12\x14\n\x0cproduct_name\x18\x04 \x01(\t\x12\r\n\x05\x66\x64_in\x18\x05 \x01(\x05\x12\x0e\n\x06\x66\x64_out\x18\x06 \x01(\x05\x12\x10\n\x08loopback\x18\x07 \x01(\x08\x62\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'startup_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _STARTUPINFO._serialized_start=47 + _STARTUPINFO._serialized_end=174 + _STARTUPINFO_DEVICE._serialized_start=115 + _STARTUPINFO_DEVICE._serialized_end=174 + _CHIPINFO._serialized_start=176 + _CHIPINFO._serialized_end=236 + _CHIP._serialized_start=239 + _CHIP._serialized_end=389 +# @@protoc_insertion_point(module_scope) diff --git a/bumble/transport/grpc_protobuf/startup_pb2.pyi b/bumble/transport/grpc_protobuf/startup_pb2.pyi new file mode 100644 index 0000000..604d915 --- /dev/null +++ b/bumble/transport/grpc_protobuf/startup_pb2.pyi @@ -0,0 +1,46 @@ +from . import common_pb2 as _common_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class Chip(_message.Message): + __slots__ = ["fd_in", "fd_out", "id", "kind", "loopback", "manufacturer", "product_name"] + FD_IN_FIELD_NUMBER: _ClassVar[int] + FD_OUT_FIELD_NUMBER: _ClassVar[int] + ID_FIELD_NUMBER: _ClassVar[int] + KIND_FIELD_NUMBER: _ClassVar[int] + LOOPBACK_FIELD_NUMBER: _ClassVar[int] + MANUFACTURER_FIELD_NUMBER: _ClassVar[int] + PRODUCT_NAME_FIELD_NUMBER: _ClassVar[int] + fd_in: int + fd_out: int + id: str + kind: _common_pb2.ChipKind + loopback: bool + manufacturer: str + product_name: str + def __init__(self, kind: _Optional[_Union[_common_pb2.ChipKind, str]] = ..., id: _Optional[str] = ..., manufacturer: _Optional[str] = ..., product_name: _Optional[str] = ..., fd_in: _Optional[int] = ..., fd_out: _Optional[int] = ..., loopback: bool = ...) -> None: ... + +class ChipInfo(_message.Message): + __slots__ = ["chip", "name"] + CHIP_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + chip: Chip + name: str + def __init__(self, name: _Optional[str] = ..., chip: _Optional[_Union[Chip, _Mapping]] = ...) -> None: ... + +class StartupInfo(_message.Message): + __slots__ = ["devices"] + class Device(_message.Message): + __slots__ = ["chips", "name"] + CHIPS_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + chips: _containers.RepeatedCompositeFieldContainer[Chip] + name: str + def __init__(self, name: _Optional[str] = ..., chips: _Optional[_Iterable[_Union[Chip, _Mapping]]] = ...) -> None: ... + DEVICES_FIELD_NUMBER: _ClassVar[int] + devices: _containers.RepeatedCompositeFieldContainer[StartupInfo.Device] + def __init__(self, devices: _Optional[_Iterable[_Union[StartupInfo.Device, _Mapping]]] = ...) -> None: ... diff --git a/bumble/transport/grpc_protobuf/startup_pb2_grpc.py b/bumble/transport/grpc_protobuf/startup_pb2_grpc.py new file mode 100644 index 0000000..2daafff --- /dev/null +++ b/bumble/transport/grpc_protobuf/startup_pb2_grpc.py @@ -0,0 +1,4 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + diff --git a/bumble/transport/hci_socket.py b/bumble/transport/hci_socket.py index f74a535..4e1ad99 100644 --- a/bumble/transport/hci_socket.py +++ b/bumble/transport/hci_socket.py @@ -40,15 +40,21 @@ async def open_hci_socket_transport(spec): or a 0-based integer to indicate the adapter number. ''' - HCI_CHANNEL_USER = 1 + HCI_CHANNEL_USER = 1 # pylint: disable=invalid-name # Create a raw HCI socket try: - hci_socket = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW | socket.SOCK_NONBLOCK, socket.BTPROTO_HCI) - except AttributeError: + hci_socket = socket.socket( + socket.AF_BLUETOOTH, + socket.SOCK_RAW | socket.SOCK_NONBLOCK, + socket.BTPROTO_HCI, + ) + except AttributeError as error: # Not supported on this platform logger.info("HCI sockets not supported on this platform") - raise Exception('Bluetooth HCI sockets not supported on this platform') + raise Exception( + 'Bluetooth HCI sockets not supported on this platform' + ) from error # Compute the adapter index if spec is None: @@ -62,20 +68,37 @@ async def open_hci_socket_transport(spec): try: ctypes.cdll.LoadLibrary('libc.so.6') libc = ctypes.CDLL('libc.so.6', use_errno=True) - except OSError: + except OSError as error: logger.info("HCI sockets not supported on this platform") - raise Exception('Bluetooth HCI sockets not supported on this platform') + raise Exception( + 'Bluetooth HCI sockets not supported on this platform' + ) from error libc.bind.argtypes = (ctypes.c_int, ctypes.POINTER(ctypes.c_char), ctypes.c_int) libc.bind.restype = ctypes.c_int - bind_address = struct.pack('<HHH', socket.AF_BLUETOOTH, adapter_index, HCI_CHANNEL_USER) - if libc.bind(hci_socket.fileno(), ctypes.create_string_buffer(bind_address), len(bind_address)) != 0: + bind_address = struct.pack( + # pylint: disable=no-member + '<HHH', + socket.AF_BLUETOOTH, + adapter_index, + HCI_CHANNEL_USER, + ) + if ( + libc.bind( + hci_socket.fileno(), + ctypes.create_string_buffer(bind_address), + len(bind_address), + ) + != 0 + ): raise IOError(ctypes.get_errno(), os.strerror(ctypes.get_errno())) class HciSocketSource(ParserSource): - def __init__(self, socket): + def __init__(self, hci_socket): super().__init__() - self.socket = socket - asyncio.get_running_loop().add_reader(socket.fileno(), self.recv_until_would_block) + self.socket = hci_socket + asyncio.get_running_loop().add_reader( + self.socket.fileno(), self.recv_until_would_block + ) def recv_until_would_block(self): logger.debug('recv until would block +++') @@ -92,9 +115,9 @@ async def open_hci_socket_transport(spec): asyncio.get_running_loop().remove_reader(self.socket.fileno()) class HciSocketSink: - def __init__(self, socket): - self.socket = socket - self.packets = collections.deque() + def __init__(self, hci_socket): + self.socket = hci_socket + self.packets = collections.deque() self.writer_added = False def send_until_would_block(self): @@ -112,9 +135,14 @@ async def open_hci_socket_transport(spec): break if self.packets: - # There's still something to send, ensure that we are monitoring the socket + # There's still something to send, ensure that we are monitoring the + # socket if not self.writer_added: - asyncio.get_running_loop().add_writer(socket.fileno(), self.send_until_would_block) + asyncio.get_running_loop().add_writer( + # pylint: disable=no-member + self.socket.fileno(), + self.send_until_would_block, + ) self.writer_added = True else: # Nothing left to send, stop monitoring the socket @@ -131,9 +159,9 @@ async def open_hci_socket_transport(spec): asyncio.get_running_loop().remove_writer(self.socket.fileno()) class HciSocketTransport(Transport): - def __init__(self, socket, source, sink): + def __init__(self, hci_socket, source, sink): super().__init__(source, sink) - self.socket = socket + self.socket = hci_socket async def close(self): logger.debug('closing HCI socket transport') diff --git a/bumble/transport/pty.py b/bumble/transport/pty.py index 2399d14..e6e2ab5 100644 --- a/bumble/transport/pty.py +++ b/bumble/transport/pty.py @@ -47,13 +47,11 @@ async def open_pty_transport(spec): tty.setraw(replica) read_transport, packet_source = await asyncio.get_running_loop().connect_read_pipe( - lambda: StreamPacketSource(), - io.open(primary, 'rb', closefd=False) + StreamPacketSource, io.open(primary, 'rb', closefd=False) ) write_transport, _ = await asyncio.get_running_loop().connect_write_pipe( - lambda: asyncio.BaseProtocol(), - io.open(primary, 'wb', closefd=False) + asyncio.BaseProtocol, io.open(primary, 'wb', closefd=False) ) packet_sink = StreamPacketSink(write_transport) diff --git a/bumble/transport/py.typed b/bumble/transport/py.typed new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/bumble/transport/py.typed diff --git a/bumble/transport/pyusb.py b/bumble/transport/pyusb.py index ea5bc0d..8ad8598 100644 --- a/bumble/transport/pyusb.py +++ b/bumble/transport/pyusb.py @@ -17,14 +17,15 @@ # ----------------------------------------------------------------------------- import asyncio import logging -import usb.core -import usb.util import threading import time -from colors import color + +import usb.core +import usb.util from .common import Transport, ParserSource from .. import hci +from ..colors import color # ----------------------------------------------------------------------------- @@ -48,25 +49,26 @@ async def open_pyusb_transport(spec): 04b4:f901 --> the BT USB dongle with vendor=04b4 and product=f901 ''' - USB_RECIPIENT_DEVICE = 0x00 - USB_REQUEST_TYPE_CLASS = 0x01 << 5 - USB_ENDPOINT_EVENTS_IN = 0x81 - USB_ENDPOINT_ACL_IN = 0x82 - USB_ENDPOINT_SCO_IN = 0x83 - USB_ENDPOINT_ACL_OUT = 0x02 + # pylint: disable=invalid-name + USB_RECIPIENT_DEVICE = 0x00 + USB_REQUEST_TYPE_CLASS = 0x01 << 5 + USB_ENDPOINT_EVENTS_IN = 0x81 + USB_ENDPOINT_ACL_IN = 0x82 + USB_ENDPOINT_SCO_IN = 0x83 + USB_ENDPOINT_ACL_OUT = 0x02 # USB_ENDPOINT_SCO_OUT = 0x03 - USB_DEVICE_CLASS_WIRELESS_CONTROLLER = 0xE0 - USB_DEVICE_SUBCLASS_RF_CONTROLLER = 0x01 + USB_DEVICE_CLASS_WIRELESS_CONTROLLER = 0xE0 + USB_DEVICE_SUBCLASS_RF_CONTROLLER = 0x01 USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER = 0x01 - READ_SIZE = 1024 + READ_SIZE = 1024 READ_TIMEOUT = 1000 class UsbPacketSink: def __init__(self, device): - self.device = device - self.thread = threading.Thread(target=self.run) - self.loop = asyncio.get_running_loop() + self.device = device + self.thread = threading.Thread(target=self.run) + self.loop = asyncio.get_running_loop() self.stop_event = None def on_packet(self, packet): @@ -80,9 +82,17 @@ async def open_pyusb_transport(spec): if packet_type == hci.HCI_ACL_DATA_PACKET: self.device.write(USB_ENDPOINT_ACL_OUT, packet[1:]) elif packet_type == hci.HCI_COMMAND_PACKET: - self.device.ctrl_transfer(USB_RECIPIENT_DEVICE | USB_REQUEST_TYPE_CLASS, 0, 0, 0, packet[1:]) + self.device.ctrl_transfer( + USB_RECIPIENT_DEVICE | USB_REQUEST_TYPE_CLASS, + 0, + 0, + 0, + packet[1:], + ) else: - logger.warning(color(f'unsupported packet type {packet_type}', 'red')) + logger.warning( + color(f'unsupported packet type {packet_type}', 'red') + ) except usb.core.USBTimeoutError: logger.warning('USB Write Timeout') except usb.core.USBError as error: @@ -100,22 +110,21 @@ async def open_pyusb_transport(spec): def run(self): while self.stop_event is None: time.sleep(1) - self.loop.call_soon_threadsafe(lambda: self.stop_event.set()) + self.loop.call_soon_threadsafe(self.stop_event.set) class UsbPacketSource(asyncio.Protocol, ParserSource): def __init__(self, device, sco_enabled): super().__init__() - self.device = device - self.loop = asyncio.get_running_loop() - self.queue = asyncio.Queue() + self.device = device + self.loop = asyncio.get_running_loop() + self.queue = asyncio.Queue() + self.dequeue_task = None self.event_thread = threading.Thread( - target=self.run, - args=(USB_ENDPOINT_EVENTS_IN, hci.HCI_EVENT_PACKET) + target=self.run, args=(USB_ENDPOINT_EVENTS_IN, hci.HCI_EVENT_PACKET) ) self.event_thread.stop_event = None self.acl_thread = threading.Thread( - target=self.run, - args=(USB_ENDPOINT_ACL_IN, hci.HCI_ACL_DATA_PACKET) + target=self.run, args=(USB_ENDPOINT_ACL_IN, hci.HCI_ACL_DATA_PACKET) ) self.acl_thread.stop_event = None @@ -124,12 +133,12 @@ async def open_pyusb_transport(spec): if sco_enabled: self.sco_thread = threading.Thread( target=self.run, - args=(USB_ENDPOINT_SCO_IN, hci.HCI_SYNCHRONOUS_DATA_PACKET) + args=(USB_ENDPOINT_SCO_IN, hci.HCI_SYNCHRONOUS_DATA_PACKET), ) self.sco_thread.stop_event = None - def data_received(self, packet): - self.parser.feed_data(packet) + def data_received(self, data): + self.parser.feed_data(data) def enqueue(self, packet): self.queue.put_nowait(packet) @@ -155,7 +164,7 @@ async def open_pyusb_transport(spec): # Create stop events and wait for them to be signaled self.event_thread.stop_event = asyncio.Event() - self.acl_thread.stop_event = asyncio.Event() + self.acl_thread.stop_event = asyncio.Event() await self.event_thread.stop_event.wait() await self.acl_thread.stop_event.wait() if self.sco_enabled: @@ -173,16 +182,17 @@ async def open_pyusb_transport(spec): except usb.core.USBTimeoutError: continue except usb.core.USBError: - # Don't log this: because pyusb doesn't really support multiple threads - # reading at the same time, we can get occasional USBError(errno=5) - # Input/Output errors reported, but they seem to be harmless. + # Don't log this: because pyusb doesn't really support multiple + # threads reading at the same time, we can get occasional + # USBError(errno=5) Input/Output errors reported, but they seem to + # be harmless. # Until support for async or multi-thread support is added to pyusb, # we'll just live with this as is... # logger.warning(f'USB read error: {error}') time.sleep(1) # Sleep one second to avoid busy looping stop_event = current_thread.stop_event - self.loop.call_soon_threadsafe(lambda: stop_event.set()) + self.loop.call_soon_threadsafe(stop_event.set) class UsbTransport(Transport): def __init__(self, device, source, sink): @@ -194,18 +204,28 @@ async def open_pyusb_transport(spec): await self.sink.stop() usb.util.release_interface(self.device, 0) + usb_find = usb.core.find + try: + import libusb_package + except ImportError: + logger.debug('libusb_package is not available') + else: + usb_find = libusb_package.find + # Find the device according to the spec moniker if ':' in spec: vendor_id, product_id = spec.split(':') - device = usb.core.find(idVendor=int(vendor_id, 16), idProduct=int(product_id, 16)) + device = usb_find(idVendor=int(vendor_id, 16), idProduct=int(product_id, 16)) else: device_index = int(spec) - devices = list(usb.core.find( - find_all = 1, - bDeviceClass = USB_DEVICE_CLASS_WIRELESS_CONTROLLER, - bDeviceSubClass = USB_DEVICE_SUBCLASS_RF_CONTROLLER, - bDeviceProtocol = USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER - )) + devices = list( + usb_find( + find_all=1, + bDeviceClass=USB_DEVICE_CLASS_WIRELESS_CONTROLLER, + bDeviceSubClass=USB_DEVICE_SUBCLASS_RF_CONTROLLER, + bDeviceProtocol=USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER, + ) + ) if len(devices) > device_index: device = devices[device_index] else: @@ -232,6 +252,7 @@ async def open_pyusb_transport(spec): # Select an alternate setting for SCO, if available sco_enabled = False + # pylint: disable=line-too-long # NOTE: this is disabled for now, because SCO with alternate settings is broken, # see: https://github.com/libusb/libusb/issues/36 # @@ -273,4 +294,4 @@ async def open_pyusb_transport(spec): packet_source.start() packet_sink.start() - return UsbTransport(device, packet_source, packet_sink)
\ No newline at end of file + return UsbTransport(device, packet_source, packet_sink) diff --git a/bumble/transport/serial.py b/bumble/transport/serial.py index b760a29..c83b605 100644 --- a/bumble/transport/serial.py +++ b/bumble/transport/serial.py @@ -60,13 +60,12 @@ async def open_serial_transport(spec): device = spec serial_transport, packet_source = await serial_asyncio.create_serial_connection( asyncio.get_running_loop(), - lambda: StreamPacketSource(), + StreamPacketSource, device, baudrate=speed, rtscts=rtscts, - dsrdtr=dsrdtr + dsrdtr=dsrdtr, ) packet_sink = StreamPacketSink(serial_transport) return Transport(packet_source, packet_sink) - diff --git a/bumble/transport/tcp_client.py b/bumble/transport/tcp_client.py index e250f25..934a521 100644 --- a/bumble/transport/tcp_client.py +++ b/bumble/transport/tcp_client.py @@ -37,13 +37,13 @@ async def open_tcp_client_transport(spec): ''' class TcpPacketSource(StreamPacketSource): - def connection_lost(self, error): - logger.debug(f'connection lost: {error}') - self.terminated.set_result(error) + def connection_lost(self, exc): + logger.debug(f'connection lost: {exc}') + self.terminated.set_result(exc) remote_host, remote_port = spec.split(':') tcp_transport, packet_source = await asyncio.get_running_loop().create_connection( - lambda: TcpPacketSource(), + TcpPacketSource, host=remote_host, port=int(remote_port), ) diff --git a/bumble/transport/tcp_server.py b/bumble/transport/tcp_server.py index 6806683..11b0453 100644 --- a/bumble/transport/tcp_server.py +++ b/bumble/transport/tcp_server.py @@ -45,12 +45,12 @@ async def open_tcp_server_transport(spec): class TcpServerProtocol: def __init__(self, packet_source, packet_sink): self.packet_source = packet_source - self.packet_sink = packet_sink + self.packet_sink = packet_sink # Called when a new connection is established def connection_made(self, transport): - peername = transport.get_extra_info('peername') - logger.debug('connection from {}'.format(peername)) + peer_name = transport.get_extra_info('peer_name') + logger.debug(f'connection from {peer_name}') self.packet_sink.transport = transport # Called when the client is disconnected @@ -78,7 +78,7 @@ async def open_tcp_server_transport(spec): local_host, local_port = spec.split(':') packet_source = StreamPacketSource() - packet_sink = TcpServerPacketSink() + packet_sink = TcpServerPacketSink() await asyncio.get_running_loop().create_server( lambda: TcpServerProtocol(packet_source, packet_sink), host=local_host if local_host != '_' else None, diff --git a/bumble/transport/udp.py b/bumble/transport/udp.py index f4c59ea..e5e26fa 100644 --- a/bumble/transport/udp.py +++ b/bumble/transport/udp.py @@ -53,10 +53,13 @@ async def open_udp_transport(spec): local, remote = spec.split(',') local_host, local_port = local.split(':') remote_host, remote_port = remote.split(':') - udp_transport, packet_source = await asyncio.get_running_loop().create_datagram_endpoint( - lambda: UdpPacketSource(), + ( + udp_transport, + packet_source, + ) = await asyncio.get_running_loop().create_datagram_endpoint( + UdpPacketSource, local_addr=(local_host, int(local_port)), - remote_addr=(remote_host, int(remote_port)) + remote_addr=(remote_host, int(remote_port)), ) packet_sink = UdpPacketSink(udp_transport) diff --git a/bumble/transport/usb.py b/bumble/transport/usb.py index dfc169f..68c5a6f 100644 --- a/bumble/transport/usb.py +++ b/bumble/transport/usb.py @@ -17,13 +17,16 @@ # ----------------------------------------------------------------------------- import asyncio import logging -import usb1 import threading import collections -from colors import color +import ctypes +import platform + +import usb1 from .common import Transport, ParserSource from .. import hci +from ..colors import color # ----------------------------------------------------------------------------- @@ -33,42 +36,88 @@ logger = logging.getLogger(__name__) # ----------------------------------------------------------------------------- +def load_libusb(): + ''' + Attempt to load the libusb-1.0 C library from libusb_package in site-packages. + If the library exists, we create a DLL object and initialize the usb1 backend. + This only needs to be done once, but before a usb1.USBContext is created. + If the library does not exists, do nothing and usb1 will search default system paths + when usb1.USBContext is created. + ''' + try: + import libusb_package + except ImportError: + logger.debug('libusb_package is not available') + else: + if libusb_path := libusb_package.get_library_path(): + logger.debug(f'loading libusb library at {libusb_path}') + dll_loader = ( + ctypes.WinDLL if platform.system() == 'Windows' else ctypes.CDLL + ) + libusb_dll = dll_loader( + str(libusb_path), use_errno=True, use_last_error=True + ) + usb1.loadLibrary(libusb_dll) + + async def open_usb_transport(spec): ''' Open a USB transport. - The parameter string has this syntax: - either <index> or <vendor>:<product>[/<serial-number>] + The moniker string has this syntax: + either <index> or + <vendor>:<product> or + <vendor>:<product>/<serial-number>] or + <vendor>:<product>#<index> With <index> as the 0-based index to select amongst all the devices that appear to be supporting Bluetooth HCI (0 being the first one), or Where <vendor> and <product> are the vendor ID and product ID in hexadecimal. The - /<serial-number> suffix max be specified when more than one device with the same - vendor and product identifiers are present. + /<serial-number> suffix or #<index> suffix max be specified when more than one + device with the same vendor and product identifiers are present. + + In addition, if the moniker ends with the symbol "!", the device will be used in + "forced" mode: + the first USB interface of the device will be used, regardless of the interface + class/subclass. + This may be useful for some devices that use a custom class/subclass but may + nonetheless work as-is. Examples: 0 --> the first BT USB dongle 04b4:f901 --> the BT USB dongle with vendor=04b4 and product=f901 - 04b4:f901/00E04C239987 --> the BT USB dongle with vendor=04b4 and product=f901 and serial number 00E04C239987 + 04b4:f901#2 --> the third USB device with vendor=04b4 and product=f901 + 04b4:f901/00E04C239987 --> the BT USB dongle with vendor=04b4 and product=f901 and + serial number 00E04C239987 + usb:0B05:17CB! --> the BT USB dongle vendor=0B05 and product=17CB, in "forced" mode. ''' - USB_RECIPIENT_DEVICE = 0x00 - USB_REQUEST_TYPE_CLASS = 0x01 << 5 - USB_ENDPOINT_EVENTS_IN = 0x81 - USB_ENDPOINT_ACL_IN = 0x82 - USB_ENDPOINT_ACL_OUT = 0x02 - USB_DEVICE_CLASS_WIRELESS_CONTROLLER = 0xE0 - USB_DEVICE_SUBCLASS_RF_CONTROLLER = 0x01 + # pylint: disable=invalid-name + USB_RECIPIENT_DEVICE = 0x00 + USB_REQUEST_TYPE_CLASS = 0x01 << 5 + USB_DEVICE_CLASS_DEVICE = 0x00 + USB_DEVICE_CLASS_WIRELESS_CONTROLLER = 0xE0 + USB_DEVICE_SUBCLASS_RF_CONTROLLER = 0x01 USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER = 0x01 + USB_ENDPOINT_TRANSFER_TYPE_BULK = 0x02 + USB_ENDPOINT_TRANSFER_TYPE_INTERRUPT = 0x03 + USB_ENDPOINT_IN = 0x80 + + USB_BT_HCI_CLASS_TUPLE = ( + USB_DEVICE_CLASS_WIRELESS_CONTROLLER, + USB_DEVICE_SUBCLASS_RF_CONTROLLER, + USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER, + ) READ_SIZE = 1024 class UsbPacketSink: - def __init__(self, device): - self.device = device - self.transfer = device.getTransfer() - self.packets = collections.deque() # Queue of packets waiting to be sent - self.loop = asyncio.get_running_loop() + def __init__(self, device, acl_out): + self.device = device + self.acl_out = acl_out + self.transfer = device.getTransfer() + self.packets = collections.deque() # Queue of packets waiting to be sent + self.loop = asyncio.get_running_loop() self.cancel_done = self.loop.create_future() - self.closed = False + self.closed = False def start(self): pass @@ -92,12 +141,15 @@ async def open_usb_transport(spec): status = transfer.getStatus() # logger.debug(f'<<< USB out transfer callback: status={status}') + # pylint: disable=no-member if status == usb1.TRANSFER_COMPLETED: self.loop.call_soon_threadsafe(self.on_packet_sent_) elif status == usb1.TRANSFER_CANCELLED: self.loop.call_soon_threadsafe(self.cancel_done.set_result, None) else: - logger.warning(color(f'!!! out transfer not completed: status={status}', 'red')) + logger.warning( + color(f'!!! out transfer not completed: status={status}', 'red') + ) def on_packet_sent_(self): if self.packets: @@ -112,32 +164,38 @@ async def open_usb_transport(spec): packet_type = packet[0] if packet_type == hci.HCI_ACL_DATA_PACKET: self.transfer.setBulk( - USB_ENDPOINT_ACL_OUT, - packet[1:], - callback=self.on_packet_sent + self.acl_out, packet[1:], callback=self.on_packet_sent ) logger.debug('submit ACL') self.transfer.submit() elif packet_type == hci.HCI_COMMAND_PACKET: self.transfer.setControl( - USB_RECIPIENT_DEVICE | USB_REQUEST_TYPE_CLASS, 0, 0, 0, + USB_RECIPIENT_DEVICE | USB_REQUEST_TYPE_CLASS, + 0, + 0, + 0, packet[1:], - callback=self.on_packet_sent + callback=self.on_packet_sent, ) logger.debug('submit COMMAND') self.transfer.submit() else: logger.warning(color(f'unsupported packet type {packet_type}', 'red')) - async def close(self): + def close(self): self.closed = True + async def terminate(self): + if not self.closed: + self.close() + # Empty the packet queue so that we don't send any more data self.packets.clear() # If we have a transfer in flight, cancel it if self.transfer.isSubmitted(): - # Try to cancel the transfer, but that may fail because it may have already completed + # Try to cancel the transfer, but that may fail because it may have + # already completed try: self.transfer.cancel() @@ -148,18 +206,23 @@ async def open_usb_transport(spec): logger.debug('OUT transfer likely already completed') class UsbPacketSource(asyncio.Protocol, ParserSource): - def __init__(self, context, device): + def __init__(self, context, device, acl_in, events_in): super().__init__() - self.context = context - self.device = device - self.loop = asyncio.get_running_loop() - self.queue = asyncio.Queue() - self.closed = False + self.context = context + self.device = device + self.acl_in = acl_in + self.events_in = events_in + self.loop = asyncio.get_running_loop() + self.queue = asyncio.Queue() + self.dequeue_task = None + self.closed = False self.event_loop_done = self.loop.create_future() self.cancel_done = { - hci.HCI_EVENT_PACKET: self.loop.create_future(), - hci.HCI_ACL_DATA_PACKET: self.loop.create_future() + hci.HCI_EVENT_PACKET: self.loop.create_future(), + hci.HCI_ACL_DATA_PACKET: self.loop.create_future(), } + self.events_in_transfer = None + self.acl_in_transfer = None # Create a thread to process events self.event_thread = threading.Thread(target=self.run) @@ -168,19 +231,19 @@ async def open_usb_transport(spec): # Set up transfer objects for input self.events_in_transfer = device.getTransfer() self.events_in_transfer.setInterrupt( - USB_ENDPOINT_EVENTS_IN, + self.events_in, READ_SIZE, callback=self.on_packet_received, - user_data=hci.HCI_EVENT_PACKET + user_data=hci.HCI_EVENT_PACKET, ) self.events_in_transfer.submit() self.acl_in_transfer = device.getTransfer() self.acl_in_transfer.setBulk( - USB_ENDPOINT_ACL_IN, + self.acl_in, READ_SIZE, callback=self.on_packet_received, - user_data=hci.HCI_ACL_DATA_PACKET + user_data=hci.HCI_ACL_DATA_PACKET, ) self.acl_in_transfer.submit() @@ -190,16 +253,28 @@ async def open_usb_transport(spec): def on_packet_received(self, transfer): packet_type = transfer.getUserData() status = transfer.getStatus() - # logger.debug(f'<<< USB IN transfer callback: status={status} packet_type={packet_type}') + # logger.debug( + # f'<<< USB IN transfer callback: status={status} ' + # f'packet_type={packet_type} ' + # f'length={transfer.getActualLength()}' + # ) + # pylint: disable=no-member if status == usb1.TRANSFER_COMPLETED: - packet = bytes([packet_type]) + transfer.getBuffer()[:transfer.getActualLength()] + packet = ( + bytes([packet_type]) + + transfer.getBuffer()[: transfer.getActualLength()] + ) self.loop.call_soon_threadsafe(self.queue.put_nowait, packet) elif status == usb1.TRANSFER_CANCELLED: - self.loop.call_soon_threadsafe(self.cancel_done[packet_type].set_result, None) + self.loop.call_soon_threadsafe( + self.cancel_done[packet_type].set_result, None + ) return else: - logger.warning(color(f'!!! transfer not completed: status={status}', 'red')) + logger.warning( + color(f'!!! transfer not completed: status={status}', 'red') + ) # Re-submit the transfer so we can receive more data transfer.submit() @@ -214,7 +289,11 @@ async def open_usb_transport(spec): def run(self): logger.debug('starting USB event loop') - while self.events_in_transfer.isSubmitted() or self.acl_in_transfer.isSubmitted(): + while ( + self.events_in_transfer.isSubmitted() + or self.acl_in_transfer.isSubmitted() + ): + # pylint: disable=no-member try: self.context.handleEvents() except usb1.USBErrorInterrupted: @@ -223,75 +302,130 @@ async def open_usb_transport(spec): logger.debug('USB event loop done') self.loop.call_soon_threadsafe(self.event_loop_done.set_result, None) - async def close(self): + def close(self): self.closed = True + + async def terminate(self): + if not self.closed: + self.close() + self.dequeue_task.cancel() # Cancel the transfers for transfer in (self.events_in_transfer, self.acl_in_transfer): if transfer.isSubmitted(): - # Try to cancel the transfer, but that may fail because it may have already completed + # Try to cancel the transfer, but that may fail because it may have + # already completed packet_type = transfer.getUserData() try: transfer.cancel() - logger.debug(f'waiting for IN[{packet_type}] transfer cancellation to be done...') + logger.debug( + f'waiting for IN[{packet_type}] transfer cancellation ' + 'to be done...' + ) await self.cancel_done[packet_type] logger.debug(f'IN[{packet_type}] transfer cancellation done') except usb1.USBError: - logger.debug(f'IN[{packet_type}] transfer likely already completed') + logger.debug( + f'IN[{packet_type}] transfer likely already completed' + ) # Wait for the thread to terminate await self.event_loop_done class UsbTransport(Transport): - def __init__(self, context, device, interface, source, sink): + def __init__(self, context, device, interface, setting, source, sink): super().__init__(source, sink) - self.context = context - self.device = device + self.context = context + self.device = device self.interface = interface # Get exclusive access device.claimInterface(interface) + # Set the alternate setting if not the default + if setting != 0: + device.setInterfaceAltSetting(interface, setting) + # The source and sink can now start source.start() sink.start() async def close(self): - await self.source.close() - await self.sink.close() + self.source.close() + self.sink.close() + await self.source.terminate() + await self.sink.terminate() self.device.releaseInterface(self.interface) self.device.close() self.context.close() # Find the device according to the spec moniker + load_libusb() context = usb1.USBContext() context.open() try: found = None + + if spec.endswith('!'): + spec = spec[:-1] + forced_mode = True + else: + forced_mode = False + if ':' in spec: vendor_id, product_id = spec.split(':') + serial_number = None + device_index = 0 if '/' in product_id: product_id, serial_number = product_id.split('/') - for device in context.getDeviceIterator(skip_on_error=True): - if ( - device.getVendorID() == int(vendor_id, 16) and - device.getProductID() == int(product_id, 16) and - device.getSerialNumber() == serial_number - ): + elif '#' in product_id: + product_id, device_index_str = product_id.split('#') + device_index = int(device_index_str) + + for device in context.getDeviceIterator(skip_on_error=True): + try: + device_serial_number = device.getSerialNumber() + except usb1.USBError: + device_serial_number = None + if ( + device.getVendorID() == int(vendor_id, 16) + and device.getProductID() == int(product_id, 16) + and (serial_number is None or serial_number == device_serial_number) + ): + if device_index == 0: found = device break - device.close() - else: - found = context.getByVendorIDAndProductID(int(vendor_id, 16), int(product_id, 16), skip_on_error=True) + device_index -= 1 + device.close() else: + # Look for a compatible device by index + def device_is_bluetooth_hci(device): + # Check if the device class indicates a match + if ( + device.getDeviceClass(), + device.getDeviceSubClass(), + device.getDeviceProtocol(), + ) == USB_BT_HCI_CLASS_TUPLE: + return True + + # If the device class is 'Device', look for a matching interface + if device.getDeviceClass() == USB_DEVICE_CLASS_DEVICE: + for configuration in device: + for interface in configuration: + for setting in interface: + if ( + setting.getClass(), + setting.getSubClass(), + setting.getProtocol(), + ) == USB_BT_HCI_CLASS_TUPLE: + return True + + return False + device_index = int(spec) for device in context.getDeviceIterator(skip_on_error=True): - if ( - device.getDeviceClass() == USB_DEVICE_CLASS_WIRELESS_CONTROLLER and - device.getDeviceSubClass() == USB_DEVICE_SUBCLASS_RF_CONTROLLER and - device.getDeviceProtocol() == USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER - ): + if device_is_bluetooth_hci(device): if device_index == 0: found = device break @@ -303,34 +437,107 @@ async def open_usb_transport(spec): raise ValueError('device not found') logger.debug(f'USB Device: {found}') + + # Look for the first interface with the right class and endpoints + def find_endpoints(device): + # pylint: disable-next=too-many-nested-blocks + for (configuration_index, configuration) in enumerate(device): + interface = None + for interface in configuration: + setting = None + for setting in interface: + if ( + not forced_mode + and ( + setting.getClass(), + setting.getSubClass(), + setting.getProtocol(), + ) + != USB_BT_HCI_CLASS_TUPLE + ): + continue + + events_in = None + acl_in = None + acl_out = None + for endpoint in setting: + attributes = endpoint.getAttributes() + address = endpoint.getAddress() + if attributes & 0x03 == USB_ENDPOINT_TRANSFER_TYPE_BULK: + if address & USB_ENDPOINT_IN and acl_in is None: + acl_in = address + elif acl_out is None: + acl_out = address + elif ( + attributes & 0x03 + == USB_ENDPOINT_TRANSFER_TYPE_INTERRUPT + ): + if address & USB_ENDPOINT_IN and events_in is None: + events_in = address + + # Return if we found all 3 endpoints + if ( + acl_in is not None + and acl_out is not None + and events_in is not None + ): + return ( + configuration_index + 1, + setting.getNumber(), + setting.getAlternateSetting(), + acl_in, + acl_out, + events_in, + ) + + logger.debug( + f'skipping configuration {configuration_index + 1} / ' + f'interface {setting.getNumber()}' + ) + + return None + + endpoints = find_endpoints(found) + if endpoints is None: + raise ValueError('no compatible interface found for device') + (configuration, interface, setting, acl_in, acl_out, events_in) = endpoints + logger.debug( + f'selected endpoints: configuration={configuration}, ' + f'interface={interface}, ' + f'setting={setting}, ' + f'acl_in=0x{acl_in:02X}, ' + f'acl_out=0x{acl_out:02X}, ' + f'events_in=0x{events_in:02X}, ' + ) + device = found.open() + # Auto-detach the kernel driver if supported + # pylint: disable=no-member + if usb1.hasCapability(usb1.CAP_SUPPORTS_DETACH_KERNEL_DRIVER): + try: + logger.debug('auto-detaching kernel driver') + device.setAutoDetachKernelDriver(True) + except usb1.USBError as error: + logger.warning(f'unable to auto-detach kernel driver: {error}') + # Set the configuration if needed try: - configuration = device.getConfiguration() - logger.debug(f'current configuration = {configuration}') + current_configuration = device.getConfiguration() + logger.debug(f'current configuration = {current_configuration}') except usb1.USBError: - try: - logger.debug('setting configuration 1') - device.setConfiguration(1) - except usb1.USBError: - logger.debug('failed to set configuration 1') - - # Use the first interface - interface = 0 + current_configuration = 0 - # Detach the kernel driver if supported and needed - if usb1.hasCapability(usb1.CAP_SUPPORTS_DETACH_KERNEL_DRIVER): + if current_configuration != configuration: try: - if device.kernelDriverActive(interface): - logger.debug("detaching kernel driver") - device.detachKernelDriver(interface) + logger.debug(f'setting configuration {configuration}') + device.setConfiguration(configuration) except usb1.USBError: - pass + logger.warning('failed to set configuration') - source = UsbPacketSource(context, device) - sink = UsbPacketSink(device) - return UsbTransport(context, device, interface, source, sink) + source = UsbPacketSource(context, device, acl_in, events_in) + sink = UsbPacketSink(device, acl_out) + return UsbTransport(context, device, interface, setting, source, sink) except usb1.USBError as error: logger.warning(color(f'!!! failed to open USB device: {error}', 'red')) context.close() diff --git a/bumble/transport/vhci.py b/bumble/transport/vhci.py index 572c31d..ec61ab4 100644 --- a/bumble/transport/vhci.py +++ b/bumble/transport/vhci.py @@ -33,7 +33,7 @@ async def open_vhci_transport(spec): path at /dev/vhci), or the path of a VHCI device ''' - HCI_VENDOR_PKT = 0xff + HCI_VENDOR_PKT = 0xFF HCI_BREDR = 0x00 # Controller type # Open the VHCI device @@ -56,4 +56,3 @@ async def open_vhci_transport(spec): transport.sink.on_packet(bytes([HCI_VENDOR_PKT, HCI_BREDR])) return transport - diff --git a/bumble/transport/ws_client.py b/bumble/transport/ws_client.py index 9ee7e49..85f6e88 100644 --- a/bumble/transport/ws_client.py +++ b/bumble/transport/ws_client.py @@ -43,7 +43,7 @@ async def open_ws_client_transport(spec): transport = PumpedTransport( PumpedPacketSource(websocket.recv), PumpedPacketSink(websocket.send), - websocket.close + websocket.close, ) transport.start() return transport diff --git a/bumble/transport/ws_server.py b/bumble/transport/ws_server.py index 3b2d15e..c7b7c6e 100644 --- a/bumble/transport/ws_server.py +++ b/bumble/transport/ws_server.py @@ -41,30 +41,36 @@ async def open_ws_server_transport(spec): class WsServerTransport(Transport): def __init__(self): - source = ParserSource() - sink = PumpedPacketSink(self.send_packet) + source = ParserSource() + sink = PumpedPacketSink(self.send_packet) self.connection = asyncio.get_running_loop().create_future() + self.server = None super().__init__(source, sink) async def serve(self, local_host, local_port): self.sink.start() + # pylint: disable-next=no-member self.server = await websockets.serve( - ws_handler = self.on_connection, - host = local_host if local_host != '_' else None, - port = int(local_port) + ws_handler=self.on_connection, + host=local_host if local_host != '_' else None, + port=int(local_port), ) logger.debug(f'websocket server ready on port {local_port}') async def on_connection(self, connection): - logger.debug(f'new connection on {connection.local_address} from {connection.remote_address}') + logger.debug( + f'new connection on {connection.local_address} ' + f'from {connection.remote_address}' + ) self.connection.set_result(connection) + # pylint: disable=no-member try: async for packet in connection: - if type(packet) is bytes: + if isinstance(packet, bytes): self.source.parser.feed_data(packet) else: - logger.warn('discarding packet: not a BINARY frame') + logger.warning('discarding packet: not a BINARY frame') except websockets.WebSocketException as error: logger.debug(f'exception while receiving packet: {error}') diff --git a/bumble/utils.py b/bumble/utils.py index 1ab3fd7..8a55684 100644 --- a/bumble/utils.py +++ b/bumble/utils.py @@ -18,10 +18,13 @@ import asyncio import logging import traceback +import collections +import sys +from typing import Awaitable, Set, TypeVar from functools import wraps -from colors import color from pyee import EventEmitter +from .colors import color # ----------------------------------------------------------------------------- # Logging @@ -33,6 +36,7 @@ logger = logging.getLogger(__name__) def setup_event_forwarding(emitter, forwarder, event_name): def emit(*args, **kwargs): forwarder.emit(event_name, *args, **kwargs) + emitter.on(event_name, emit) @@ -43,6 +47,8 @@ def composite_listener(cls): registers/deregisters all methods named `on_<event_name>` as a listener for the <event_name> event with an emitter. """ + # pylint: disable=protected-access + def register(self, emitter): for method_name in dir(cls): if method_name.startswith('on_'): @@ -53,13 +59,47 @@ def composite_listener(cls): if method_name.startswith('on_'): emitter.remove_listener(method_name[3:], getattr(self, method_name)) - cls._bumble_register_composite = register + cls._bumble_register_composite = register cls._bumble_deregister_composite = deregister return cls # ----------------------------------------------------------------------------- -class CompositeEventEmitter(EventEmitter): +_T = TypeVar('_T') + + +class AbortableEventEmitter(EventEmitter): + def abort_on(self, event: str, awaitable: Awaitable[_T]) -> Awaitable[_T]: + """ + Set a coroutine or future to abort when an event occur. + """ + future = asyncio.ensure_future(awaitable) + if future.done(): + return future + + def on_event(*_): + if future.done(): + return + msg = f'abort: {event} event occurred.' + if isinstance(future, asyncio.Task): + # python < 3.9 does not support passing a message on `Task.cancel` + if sys.version_info < (3, 9, 0): + future.cancel() + else: + future.cancel(msg) + else: + future.set_exception(asyncio.CancelledError(msg)) + + def on_done(_): + self.remove_listener(event, on_event) + + self.on(event, on_event) + future.add_done_callback(on_done) + return future + + +# ----------------------------------------------------------------------------- +class CompositeEventEmitter(AbortableEventEmitter): def __init__(self): super().__init__() self._listener = None @@ -70,6 +110,7 @@ class CompositeEventEmitter(EventEmitter): @listener.setter def listener(self, listener): + # pylint: disable=protected-access if self._listener: # Call the deregistration methods for each base class that has them for cls in self._listener.__class__.mro(): @@ -109,11 +150,16 @@ class AsyncRunner: try: await item except Exception as error: - logger.warning(f'{color("!!! Exception in work queue:", "red")} {error}') + logger.warning( + f'{color("!!! Exception in work queue:", "red")} {error}' + ) # Shared default queue default_queue = WorkQueue() + # Shared set of running tasks + running_tasks: Set[Awaitable] = set() + @staticmethod def run_in_task(queue=None): """ @@ -130,7 +176,10 @@ class AsyncRunner: try: await coroutine except Exception: - logger.warning(f'{color("!!! Exception in wrapper:", "red")} {traceback.format_exc()}') + logger.warning( + f'{color("!!! Exception in wrapper:", "red")} ' + f'{traceback.format_exc()}' + ) asyncio.create_task(run()) else: @@ -140,3 +189,116 @@ class AsyncRunner: return wrapper return decorator + + @staticmethod + def spawn(coroutine): + """ + Spawn a task to run a coroutine in a "fire and forget" mode. + + Using this method instead of just calling `asyncio.create_task(coroutine)` + is necessary when you don't keep a reference to the task, because `asyncio` + only keeps weak references to alive tasks. + """ + task = asyncio.create_task(coroutine) + AsyncRunner.running_tasks.add(task) + task.add_done_callback(AsyncRunner.running_tasks.remove) + + +# ----------------------------------------------------------------------------- +class FlowControlAsyncPipe: + """ + Asyncio pipe with flow control. When writing to the pipe, the source is + paused (by calling a function passed in when the pipe is created) if the + amount of queued data exceeds a specified threshold. + """ + + def __init__( + self, + pause_source, + resume_source, + write_to_sink=None, + drain_sink=None, + threshold=0, + ): + self.pause_source = pause_source + self.resume_source = resume_source + self.write_to_sink = write_to_sink + self.drain_sink = drain_sink + self.threshold = threshold + self.queue = collections.deque() # Queue of packets + self.queued_bytes = 0 # Number of bytes in the queue + self.ready_to_pump = asyncio.Event() + self.paused = False + self.source_paused = False + self.pump_task = None + + def start(self): + if self.pump_task is None: + self.pump_task = asyncio.create_task(self.pump()) + + self.check_pump() + + def stop(self): + if self.pump_task is not None: + self.pump_task.cancel() + self.pump_task = None + + def write(self, packet): + self.queued_bytes += len(packet) + self.queue.append(packet) + + # Pause the source if we're over the threshold + if self.queued_bytes > self.threshold and not self.source_paused: + logger.debug(f'pausing source (queued={self.queued_bytes})') + self.pause_source() + self.source_paused = True + + self.check_pump() + + def pause(self): + if not self.paused: + self.paused = True + if not self.source_paused: + self.pause_source() + self.source_paused = True + self.check_pump() + + def resume(self): + if self.paused: + self.paused = False + if self.source_paused: + self.resume_source() + self.source_paused = False + self.check_pump() + + def can_pump(self): + return self.queue and not self.paused and self.write_to_sink is not None + + def check_pump(self): + if self.can_pump(): + self.ready_to_pump.set() + else: + self.ready_to_pump.clear() + + async def pump(self): + while True: + # Wait until we can try to pump packets + await self.ready_to_pump.wait() + + # Try to pump a packet + if self.can_pump(): + packet = self.queue.pop() + self.write_to_sink(packet) + self.queued_bytes -= len(packet) + + # Drain the sink if we can + if self.drain_sink: + await self.drain_sink() + + # Check if we can accept more + if self.queued_bytes <= self.threshold and self.source_paused: + logger.debug(f'resuming source (queued={self.queued_bytes})') + self.source_paused = False + self.resume_source() + + self.check_pump() diff --git a/docs/README.md b/docs/README.md index ca7d4c0..cf32226 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,7 +2,7 @@ Bumble Documentation ==================== The documentation consists of a collection of markdown text files, with the root of the file -hierarchy at `docs/mkdocs/src`, starting with `docs/mkdocs/src/index.md`. +hierarchy at `docs/mkdocs/src`, starting with `docs/mkdocs/src/index.md`. You can read the documentation as text, with any text viewer or your favorite markdown viewer, or generate a static HTML "site" using `mkdocs`, which you can then open with any browser. @@ -14,9 +14,9 @@ The `mkdocs` directory contains all the data (actual documentation) and metadata `mkdocs/mkdocs.yml` contains the site configuration. `mkdocs/src/` is the directory where the actual documentation text, in markdown format, is located. -To build, from the project's root directory: +To build, from the project's root directory: ``` -$ mkdocs build -f docs/mkdocs/mkdocs.yml +$ mkdocs build -f docs/mkdocs/mkdocs.yml ``` You can then open `docs/mkdocs/site/index.html` with any web browser. diff --git a/docs/images/logo.vectornator/Artboard0.json b/docs/images/logo.vectornator/Artboard0.json index eddf68a..f10d409 100644 --- a/docs/images/logo.vectornator/Artboard0.json +++ b/docs/images/logo.vectornator/Artboard0.json @@ -1 +1 @@ -{"layers":[{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-5.882939210360405,25.513785542788241],"opacity":1,"blur":0,"isLocked":false,"gid":56,"smootheningRate":0,"initialPoint":[-5.882939210360405,25.513785542788241],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[225.48442074400293,105.07566425478205],"opacity":1,"blur":0,"isLocked":false,"gid":27,"smootheningRate":0,"initialPoint":[225.48442074400293,105.07566425478205],"creationPoints":[],"group":{"elements":[{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[225.48442074400293,180.0751680461143],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,180.0751680461143],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[225.48442074400293,180.0751680461143],"nextPoint":[-2.4497734478039774,-990.41770428277562]},{"outPoint":[375.4834254415108,180.0751680461143],"reflectionModeOverride":0,"anchorPoint":[375.4834254415108,180.0751680461143],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[375.4834254415108,180.0751680461143],"nextPoint":[-2.4497734478039774,-990.41770428277562]},{"outPoint":[375.4834254415108,430.07351680205772],"reflectionModeOverride":0,"anchorPoint":[375.4834254415108,430.07351680205772],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[375.4834254415108,430.07351680205772],"nextPoint":[-2.4497734478039774,-990.41770428277562]},{"outPoint":[225.48442074400293,430.07351680205772],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,430.07351680205772],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[225.48442074400293,430.07351680205772],"nextPoint":[-2.4497734478039774,-990.41770428277562]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[375.4834254415108,430.07351680205772],"opacity":1,"blur":0,"isLocked":false,"gid":22,"smootheningRate":0,"initialPoint":[225.48442074400293,180.0751680461143],"creationPoints":[],"name":"(rectangle)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[225.48442074400293,138.65412011223214],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,180.07518887113827],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[225.48442074400293,221.49625763004531],"nextPoint":[-235.35384472404451,-574.6867431949596]},{"outPoint":[341.90501411926641,105.07566425478205],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,105.07566425478205],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[259.06287660145324,105.07566425478205],"nextPoint":[-235.35384472404451,-574.6867431949596]},{"outPoint":[375.4834230115174,221.49625763004531],"reflectionModeOverride":0,"anchorPoint":[375.4834230115174,180.07518887113827],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[375.4834230115174,138.65412011223214],"nextPoint":[-235.35384472404451,-574.6867431949596]},{"outPoint":[259.06287660145324,255.0746665222963],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,255.0746665222963],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[341.90501411926641,255.0746665222963],"nextPoint":[-235.35384472404451,-574.6867431949596]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-235.35384472404451,-574.6867431949596],"opacity":1,"blur":0,"isLocked":false,"gid":24,"smootheningRate":0,"initialPoint":[-235.35384472404451,-574.6867431949596],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[225.48442074400293,388.65246608333962],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,430.07353484224666],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[225.48442074400293,471.49460360115279],"nextPoint":[-235.35384472404451,-324.68839722385167]},{"outPoint":[341.90501411926641,355.07401022588954],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,355.07401022588954],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[259.06287660145324,355.07401022588954],"nextPoint":[-235.35384472404451,-324.68839722385167]},{"outPoint":[375.4834230115174,471.49460360115279],"reflectionModeOverride":0,"anchorPoint":[375.4834230115174,430.07353484224666],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[375.4834230115174,388.65246608333962],"nextPoint":[-235.35384472404451,-324.68839722385167]},{"outPoint":[259.06287660145324,505.07301249340378],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,505.07301249340378],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[341.90501411926641,505.07301249340378],"nextPoint":[-235.35384472404451,-324.68839722385167]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-235.35384472404451,-324.68839722385167],"opacity":1,"blur":0,"isLocked":false,"gid":25,"smootheningRate":0,"initialPoint":[-235.35384472404451,-324.68839722385167],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[235.48435458284723,179.89237563632139],"reflectionModeOverride":0,"anchorPoint":[235.48435458284723,179.89237563632139],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[235.48435458284723,179.89237563632139],"nextPoint":[37.941384471822971,-990.60049669256784]},{"outPoint":[365.48349317943928,179.89237563632139],"reflectionModeOverride":0,"anchorPoint":[365.48349317943928,179.89237563632139],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[365.48349317943928,179.89237563632139],"nextPoint":[37.941384471822971,-990.60049669256784]},{"outPoint":[365.48349317943928,429.89072439226481],"reflectionModeOverride":0,"anchorPoint":[365.48349317943928,429.89072439226481],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[365.48349317943928,429.89072439226481],"nextPoint":[37.941384471822971,-990.60049669256784]},{"outPoint":[235.48435458284723,429.89072439226481],"reflectionModeOverride":0,"anchorPoint":[235.48435458284723,429.89072439226481],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[235.48435458284723,429.89072439226481],"nextPoint":[37.941384471822971,-990.60049669256784]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[365.48349317943928,429.89072439226481],"opacity":1,"blur":0,"isLocked":false,"gid":26,"smootheningRate":0,"initialPoint":[235.48435458284723,179.89237563632139],"creationPoints":[],"name":"(rectangle)"}]},"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[35.772159785141525,199.98637434514319],"reflectionModeOverride":0,"anchorPoint":[35.772159785141525,199.98637434514319],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[35.772159785141525,199.98637434514319],"nextPoint":[16.704525477792117,37.513706149401855]},{"outPoint":[159.56661941832897,334.72204728763415],"reflectionModeOverride":0,"anchorPoint":[159.56661941832897,334.72204728763415],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[159.56661941832897,334.72204728763415],"nextPoint":[16.704525477792117,37.513706149401855]},{"outPoint":[299.28226312627601,200.42272477566019],"reflectionModeOverride":0,"anchorPoint":[299.28226312627601,200.42272477566019],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[299.28226312627601,200.42272477566019],"nextPoint":[16.704525477792117,37.513706149401855]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[16.704525477792117,37.513706149401855],"opacity":1,"blur":0,"isLocked":false,"gid":29,"smootheningRate":0,"initialPoint":[16.704525477792117,37.513706149401855],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[316.02230657990947,197.89539868947611],"reflectionModeOverride":0,"anchorPoint":[316.02230657990947,197.89539868947611],"cornerRadius":0,"prevPoint":[296.695700377354,37.472065800512951],"inPoint":[316.02230657990947,197.89539868947611],"nextPoint":[296.695700377354,37.472065800512951]},{"outPoint":[441.49811195900963,330.9315945770885],"reflectionModeOverride":0,"anchorPoint":[441.49811195900963,330.9315945770885],"cornerRadius":0,"prevPoint":[296.695700377354,37.472065800512951],"inPoint":[441.49811195900963,330.9315945770885],"nextPoint":[296.695700377354,37.472065800512951]},{"outPoint":[583.11133899889126,198.32624525061453],"reflectionModeOverride":0,"anchorPoint":[583.11133899889126,198.32624525061453],"cornerRadius":0,"prevPoint":[296.695700377354,37.472065800512951],"inPoint":[583.11133899889126,198.32624525061453],"nextPoint":[296.695700377354,37.472065800512951]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[296.695700377354,37.472065800512951],"opacity":1,"blur":0,"isLocked":false,"gid":30,"smootheningRate":0,"initialPoint":[296.695700377354,37.472065800512951],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[454.65560248608688,45.627293553562936],"reflectionModeOverride":0,"anchorPoint":[454.65560248608688,45.627293553562936],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[454.65560248608688,45.627293553562936],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[162.17037499378046,339.70097131047794],"reflectionModeOverride":0,"anchorPoint":[162.17037499378046,339.70097131047794],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[162.17037499378046,339.70097131047794],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[16.450758016910754,190.92333427643769],"reflectionModeOverride":0,"anchorPoint":[16.450758016910754,190.92333427643769],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[16.450758016910754,190.92333427643769],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[586.27726302866802,193.75471648932478],"reflectionModeOverride":0,"anchorPoint":[586.27726302866802,193.75471648932478],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[586.27726302866802,193.75471648932478],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[444.36970577266027,335.60394507941498],"reflectionModeOverride":0,"anchorPoint":[444.36970577266027,335.60394507941498],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[444.36970577266027,335.60394507941498],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[155.71179749272039,46.932845447085015],"reflectionModeOverride":0,"anchorPoint":[155.71179749272039,46.932845447085015],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[155.71179749272039,46.932845447085015],"nextPoint":[971.93140466738657,-224.79785180257943]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":90,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[1132.6259120879768,-222.31407294998928],"opacity":1,"blur":0,"isLocked":false,"gid":21,"smootheningRate":0,"initialPoint":[1132.6259120879768,-222.31407294998928],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[228.4683004292159,309.55668062155007],"reflectionModeOverride":0,"anchorPoint":[228.4683004292159,309.55668062155007],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[228.4683004292159,309.55668062155007],"nextPoint":[16.704525477792117,37.513706149401855]},{"outPoint":[371.51081106842264,309.55668062155007],"reflectionModeOverride":0,"anchorPoint":[371.51081106842264,309.55668062155007],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[371.51081106842264,309.55668062155007],"nextPoint":[16.704525477792117,37.513706149401855]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[16.704525477792117,37.513706149401855],"opacity":1,"blur":0,"isLocked":false,"gid":31,"smootheningRate":0,"initialPoint":[16.704525477792117,37.513706149401855],"creationPoints":[],"name":"(line)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[228.4683004292159,412.55599916164635],"reflectionModeOverride":0,"anchorPoint":[228.4683004292159,412.55599916164635],"cornerRadius":0,"prevPoint":[16.704525477792117,140.51302468949859],"inPoint":[228.4683004292159,412.55599916164635],"nextPoint":[16.704525477792117,140.51302468949859]},{"outPoint":[371.51081106842264,412.55599916164635],"reflectionModeOverride":0,"anchorPoint":[371.51081106842264,412.55599916164635],"cornerRadius":0,"prevPoint":[16.704525477792117,140.51302468949859],"inPoint":[371.51081106842264,412.55599916164635],"nextPoint":[16.704525477792117,140.51302468949859]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[16.704525477792117,140.51302468949859],"opacity":1,"blur":0,"isLocked":false,"gid":32,"smootheningRate":0,"initialPoint":[16.704525477792117,140.51302468949859],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[2.7206907461103356,25.513785542788241],"opacity":1,"blur":0,"isLocked":false,"gid":55,"smootheningRate":0,"initialPoint":[2.7206907461103356,25.513785542788241],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300.38008332937886,557.84279577418272],"reflectionModeOverride":0,"anchorPoint":[300.38008332937886,557.84279577418272],"cornerRadius":0,"prevPoint":[4.8049994992771872,-1.0129103054042616],"inPoint":[300.87435644597588,529.50877422158635],"nextPoint":[4.8049994992771872,-1.0129103054042616]},{"outPoint":[300.43161232869249,507.2021517541192],"reflectionModeOverride":0,"anchorPoint":[300.43161232869249,507.2021517541192],"cornerRadius":0,"prevPoint":[-12.365577443621419,-27.342871697380019],"inPoint":[300.43161232869249,507.2021517541192],"nextPoint":[-12.365577443621419,-27.342871697380019]},{"outPoint":[315.7481814828169,517.26662181075551],"reflectionModeOverride":0,"anchorPoint":[336.28774305214836,507.15563112854068],"cornerRadius":0,"prevPoint":[2.7206907461103356,24.978316814837285],"inPoint":[336.28774305214836,507.15563112854068],"nextPoint":[2.7206907461103356,24.978316814837285]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[4.9686415433105822,12.610834591390244],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[4.9686415433105822,12.610834591390244],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300.44667602585685,557.84279577418272],"reflectionModeOverride":0,"anchorPoint":[300.44667602585685,557.84279577418272],"cornerRadius":0,"prevPoint":[596.02175985595829,-1.0129103054042616],"inPoint":[299.95240290925983,529.50877422158635],"nextPoint":[596.02175985595829,-1.0129103054042616]},{"outPoint":[300.39514702654321,507.2021517541192],"reflectionModeOverride":0,"anchorPoint":[300.39514702654321,507.2021517541192],"cornerRadius":0,"prevPoint":[613.1923367988569,-27.342871697380019],"inPoint":[300.39514702654321,507.2021517541192],"nextPoint":[613.1923367988569,-27.342871697380019]},{"outPoint":[285.07857787241835,517.26662181075551],"reflectionModeOverride":0,"anchorPoint":[264.53901630308735,507.15563112854068],"cornerRadius":0,"prevPoint":[598.10606860912537,24.978316814837285],"inPoint":[264.53901630308735,507.15563112854068],"nextPoint":[598.10606860912537,24.978316814837285]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[595.85811781192467,12.818230389370797],"opacity":1,"blur":0,"isLocked":false,"gid":54,"smootheningRate":0,"initialPoint":[595.85811781192467,12.818230389370797],"creationPoints":[],"name":"(curve)"}]},"name":"(curve)"}]},"name":"(curve)"}],"isExpanded":false,"isLocked":false,"isVisible":true,"opacity":1,"gid":4,"name":"Layer 1"}],"frame":{"y":0,"x":0,"width":600,"height":600},"title":"Mac App icon","activeLayerIndex":0,"settings":{"gridSpacing":20,"gridAngle":45,"backgroundColor":{"b":1,"s":0,"h":0,"a":1},"gridMode":0,"isGridVisible":false},"guideLayer":{"isExpanded":false,"elements":[],"isLocked":false,"defaultName":"Guides","isVisible":true,"opacity":1,"name":"Guides","gid":5},"gid":3}
\ No newline at end of file +{"layers":[{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-5.882939210360405,25.513785542788241],"opacity":1,"blur":0,"isLocked":false,"gid":56,"smootheningRate":0,"initialPoint":[-5.882939210360405,25.513785542788241],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[225.48442074400293,105.07566425478205],"opacity":1,"blur":0,"isLocked":false,"gid":27,"smootheningRate":0,"initialPoint":[225.48442074400293,105.07566425478205],"creationPoints":[],"group":{"elements":[{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[225.48442074400293,180.0751680461143],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,180.0751680461143],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[225.48442074400293,180.0751680461143],"nextPoint":[-2.4497734478039774,-990.41770428277562]},{"outPoint":[375.4834254415108,180.0751680461143],"reflectionModeOverride":0,"anchorPoint":[375.4834254415108,180.0751680461143],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[375.4834254415108,180.0751680461143],"nextPoint":[-2.4497734478039774,-990.41770428277562]},{"outPoint":[375.4834254415108,430.07351680205772],"reflectionModeOverride":0,"anchorPoint":[375.4834254415108,430.07351680205772],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[375.4834254415108,430.07351680205772],"nextPoint":[-2.4497734478039774,-990.41770428277562]},{"outPoint":[225.48442074400293,430.07351680205772],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,430.07351680205772],"cornerRadius":0,"prevPoint":[-2.4497734478039774,-990.41770428277562],"inPoint":[225.48442074400293,430.07351680205772],"nextPoint":[-2.4497734478039774,-990.41770428277562]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[375.4834254415108,430.07351680205772],"opacity":1,"blur":0,"isLocked":false,"gid":22,"smootheningRate":0,"initialPoint":[225.48442074400293,180.0751680461143],"creationPoints":[],"name":"(rectangle)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[225.48442074400293,138.65412011223214],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,180.07518887113827],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[225.48442074400293,221.49625763004531],"nextPoint":[-235.35384472404451,-574.6867431949596]},{"outPoint":[341.90501411926641,105.07566425478205],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,105.07566425478205],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[259.06287660145324,105.07566425478205],"nextPoint":[-235.35384472404451,-574.6867431949596]},{"outPoint":[375.4834230115174,221.49625763004531],"reflectionModeOverride":0,"anchorPoint":[375.4834230115174,180.07518887113827],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[375.4834230115174,138.65412011223214],"nextPoint":[-235.35384472404451,-574.6867431949596]},{"outPoint":[259.06287660145324,255.0746665222963],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,255.0746665222963],"cornerRadius":0,"prevPoint":[-235.35384472404451,-574.6867431949596],"inPoint":[341.90501411926641,255.0746665222963],"nextPoint":[-235.35384472404451,-574.6867431949596]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-235.35384472404451,-574.6867431949596],"opacity":1,"blur":0,"isLocked":false,"gid":24,"smootheningRate":0,"initialPoint":[-235.35384472404451,-574.6867431949596],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[225.48442074400293,388.65246608333962],"reflectionModeOverride":0,"anchorPoint":[225.48442074400293,430.07353484224666],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[225.48442074400293,471.49460360115279],"nextPoint":[-235.35384472404451,-324.68839722385167]},{"outPoint":[341.90501411926641,355.07401022588954],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,355.07401022588954],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[259.06287660145324,355.07401022588954],"nextPoint":[-235.35384472404451,-324.68839722385167]},{"outPoint":[375.4834230115174,471.49460360115279],"reflectionModeOverride":0,"anchorPoint":[375.4834230115174,430.07353484224666],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[375.4834230115174,388.65246608333962],"nextPoint":[-235.35384472404451,-324.68839722385167]},{"outPoint":[259.06287660145324,505.07301249340378],"reflectionModeOverride":0,"anchorPoint":[300.48394536035983,505.07301249340378],"cornerRadius":0,"prevPoint":[-235.35384472404451,-324.68839722385167],"inPoint":[341.90501411926641,505.07301249340378],"nextPoint":[-235.35384472404451,-324.68839722385167]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-235.35384472404451,-324.68839722385167],"opacity":1,"blur":0,"isLocked":false,"gid":25,"smootheningRate":0,"initialPoint":[-235.35384472404451,-324.68839722385167],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[235.48435458284723,179.89237563632139],"reflectionModeOverride":0,"anchorPoint":[235.48435458284723,179.89237563632139],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[235.48435458284723,179.89237563632139],"nextPoint":[37.941384471822971,-990.60049669256784]},{"outPoint":[365.48349317943928,179.89237563632139],"reflectionModeOverride":0,"anchorPoint":[365.48349317943928,179.89237563632139],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[365.48349317943928,179.89237563632139],"nextPoint":[37.941384471822971,-990.60049669256784]},{"outPoint":[365.48349317943928,429.89072439226481],"reflectionModeOverride":0,"anchorPoint":[365.48349317943928,429.89072439226481],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[365.48349317943928,429.89072439226481],"nextPoint":[37.941384471822971,-990.60049669256784]},{"outPoint":[235.48435458284723,429.89072439226481],"reflectionModeOverride":0,"anchorPoint":[235.48435458284723,429.89072439226481],"cornerRadius":0,"prevPoint":[37.941384471822971,-990.60049669256784],"inPoint":[235.48435458284723,429.89072439226481],"nextPoint":[37.941384471822971,-990.60049669256784]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[365.48349317943928,429.89072439226481],"opacity":1,"blur":0,"isLocked":false,"gid":26,"smootheningRate":0,"initialPoint":[235.48435458284723,179.89237563632139],"creationPoints":[],"name":"(rectangle)"}]},"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[35.772159785141525,199.98637434514319],"reflectionModeOverride":0,"anchorPoint":[35.772159785141525,199.98637434514319],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[35.772159785141525,199.98637434514319],"nextPoint":[16.704525477792117,37.513706149401855]},{"outPoint":[159.56661941832897,334.72204728763415],"reflectionModeOverride":0,"anchorPoint":[159.56661941832897,334.72204728763415],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[159.56661941832897,334.72204728763415],"nextPoint":[16.704525477792117,37.513706149401855]},{"outPoint":[299.28226312627601,200.42272477566019],"reflectionModeOverride":0,"anchorPoint":[299.28226312627601,200.42272477566019],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[299.28226312627601,200.42272477566019],"nextPoint":[16.704525477792117,37.513706149401855]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[16.704525477792117,37.513706149401855],"opacity":1,"blur":0,"isLocked":false,"gid":29,"smootheningRate":0,"initialPoint":[16.704525477792117,37.513706149401855],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[316.02230657990947,197.89539868947611],"reflectionModeOverride":0,"anchorPoint":[316.02230657990947,197.89539868947611],"cornerRadius":0,"prevPoint":[296.695700377354,37.472065800512951],"inPoint":[316.02230657990947,197.89539868947611],"nextPoint":[296.695700377354,37.472065800512951]},{"outPoint":[441.49811195900963,330.9315945770885],"reflectionModeOverride":0,"anchorPoint":[441.49811195900963,330.9315945770885],"cornerRadius":0,"prevPoint":[296.695700377354,37.472065800512951],"inPoint":[441.49811195900963,330.9315945770885],"nextPoint":[296.695700377354,37.472065800512951]},{"outPoint":[583.11133899889126,198.32624525061453],"reflectionModeOverride":0,"anchorPoint":[583.11133899889126,198.32624525061453],"cornerRadius":0,"prevPoint":[296.695700377354,37.472065800512951],"inPoint":[583.11133899889126,198.32624525061453],"nextPoint":[296.695700377354,37.472065800512951]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[296.695700377354,37.472065800512951],"opacity":1,"blur":0,"isLocked":false,"gid":30,"smootheningRate":0,"initialPoint":[296.695700377354,37.472065800512951],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[454.65560248608688,45.627293553562936],"reflectionModeOverride":0,"anchorPoint":[454.65560248608688,45.627293553562936],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[454.65560248608688,45.627293553562936],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[162.17037499378046,339.70097131047794],"reflectionModeOverride":0,"anchorPoint":[162.17037499378046,339.70097131047794],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[162.17037499378046,339.70097131047794],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[16.450758016910754,190.92333427643769],"reflectionModeOverride":0,"anchorPoint":[16.450758016910754,190.92333427643769],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[16.450758016910754,190.92333427643769],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[586.27726302866802,193.75471648932478],"reflectionModeOverride":0,"anchorPoint":[586.27726302866802,193.75471648932478],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[586.27726302866802,193.75471648932478],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[444.36970577266027,335.60394507941498],"reflectionModeOverride":0,"anchorPoint":[444.36970577266027,335.60394507941498],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[444.36970577266027,335.60394507941498],"nextPoint":[971.93140466738657,-224.79785180257943]},{"outPoint":[155.71179749272039,46.932845447085015],"reflectionModeOverride":0,"anchorPoint":[155.71179749272039,46.932845447085015],"cornerRadius":0,"prevPoint":[971.93140466738657,-224.79785180257943],"inPoint":[155.71179749272039,46.932845447085015],"nextPoint":[971.93140466738657,-224.79785180257943]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":90,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[1132.6259120879768,-222.31407294998928],"opacity":1,"blur":0,"isLocked":false,"gid":21,"smootheningRate":0,"initialPoint":[1132.6259120879768,-222.31407294998928],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[228.4683004292159,309.55668062155007],"reflectionModeOverride":0,"anchorPoint":[228.4683004292159,309.55668062155007],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[228.4683004292159,309.55668062155007],"nextPoint":[16.704525477792117,37.513706149401855]},{"outPoint":[371.51081106842264,309.55668062155007],"reflectionModeOverride":0,"anchorPoint":[371.51081106842264,309.55668062155007],"cornerRadius":0,"prevPoint":[16.704525477792117,37.513706149401855],"inPoint":[371.51081106842264,309.55668062155007],"nextPoint":[16.704525477792117,37.513706149401855]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[16.704525477792117,37.513706149401855],"opacity":1,"blur":0,"isLocked":false,"gid":31,"smootheningRate":0,"initialPoint":[16.704525477792117,37.513706149401855],"creationPoints":[],"name":"(line)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[228.4683004292159,412.55599916164635],"reflectionModeOverride":0,"anchorPoint":[228.4683004292159,412.55599916164635],"cornerRadius":0,"prevPoint":[16.704525477792117,140.51302468949859],"inPoint":[228.4683004292159,412.55599916164635],"nextPoint":[16.704525477792117,140.51302468949859]},{"outPoint":[371.51081106842264,412.55599916164635],"reflectionModeOverride":0,"anchorPoint":[371.51081106842264,412.55599916164635],"cornerRadius":0,"prevPoint":[16.704525477792117,140.51302468949859],"inPoint":[371.51081106842264,412.55599916164635],"nextPoint":[16.704525477792117,140.51302468949859]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[16.704525477792117,140.51302468949859],"opacity":1,"blur":0,"isLocked":false,"gid":32,"smootheningRate":0,"initialPoint":[16.704525477792117,140.51302468949859],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[2.7206907461103356,25.513785542788241],"opacity":1,"blur":0,"isLocked":false,"gid":55,"smootheningRate":0,"initialPoint":[2.7206907461103356,25.513785542788241],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300.38008332937886,557.84279577418272],"reflectionModeOverride":0,"anchorPoint":[300.38008332937886,557.84279577418272],"cornerRadius":0,"prevPoint":[4.8049994992771872,-1.0129103054042616],"inPoint":[300.87435644597588,529.50877422158635],"nextPoint":[4.8049994992771872,-1.0129103054042616]},{"outPoint":[300.43161232869249,507.2021517541192],"reflectionModeOverride":0,"anchorPoint":[300.43161232869249,507.2021517541192],"cornerRadius":0,"prevPoint":[-12.365577443621419,-27.342871697380019],"inPoint":[300.43161232869249,507.2021517541192],"nextPoint":[-12.365577443621419,-27.342871697380019]},{"outPoint":[315.7481814828169,517.26662181075551],"reflectionModeOverride":0,"anchorPoint":[336.28774305214836,507.15563112854068],"cornerRadius":0,"prevPoint":[2.7206907461103356,24.978316814837285],"inPoint":[336.28774305214836,507.15563112854068],"nextPoint":[2.7206907461103356,24.978316814837285]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[4.9686415433105822,12.610834591390244],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[4.9686415433105822,12.610834591390244],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300.44667602585685,557.84279577418272],"reflectionModeOverride":0,"anchorPoint":[300.44667602585685,557.84279577418272],"cornerRadius":0,"prevPoint":[596.02175985595829,-1.0129103054042616],"inPoint":[299.95240290925983,529.50877422158635],"nextPoint":[596.02175985595829,-1.0129103054042616]},{"outPoint":[300.39514702654321,507.2021517541192],"reflectionModeOverride":0,"anchorPoint":[300.39514702654321,507.2021517541192],"cornerRadius":0,"prevPoint":[613.1923367988569,-27.342871697380019],"inPoint":[300.39514702654321,507.2021517541192],"nextPoint":[613.1923367988569,-27.342871697380019]},{"outPoint":[285.07857787241835,517.26662181075551],"reflectionModeOverride":0,"anchorPoint":[264.53901630308735,507.15563112854068],"cornerRadius":0,"prevPoint":[598.10606860912537,24.978316814837285],"inPoint":[264.53901630308735,507.15563112854068],"nextPoint":[598.10606860912537,24.978316814837285]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[595.85811781192467,12.818230389370797],"opacity":1,"blur":0,"isLocked":false,"gid":54,"smootheningRate":0,"initialPoint":[595.85811781192467,12.818230389370797],"creationPoints":[],"name":"(curve)"}]},"name":"(curve)"}]},"name":"(curve)"}],"isExpanded":false,"isLocked":false,"isVisible":true,"opacity":1,"gid":4,"name":"Layer 1"}],"frame":{"y":0,"x":0,"width":600,"height":600},"title":"Mac App icon","activeLayerIndex":0,"settings":{"gridSpacing":20,"gridAngle":45,"backgroundColor":{"b":1,"s":0,"h":0,"a":1},"gridMode":0,"isGridVisible":false},"guideLayer":{"isExpanded":false,"elements":[],"isLocked":false,"defaultName":"Guides","isVisible":true,"opacity":1,"name":"Guides","gid":5},"gid":3} diff --git a/docs/images/logo.vectornator/Document.json b/docs/images/logo.vectornator/Document.json index ac7714f..6439b5c 100644 --- a/docs/images/logo.vectornator/Document.json +++ b/docs/images/logo.vectornator/Document.json @@ -1 +1 @@ -{"date":644900643.85054696,"appVersion":"4.1.5","drawing":{"modificationDate":644894800.328192,"activeArtboardIndex":0,"settings":{"outlineMode":false,"isolateActiveLayer":false,"snapToEdges":false,"snapToPoints":false,"guidesVisible":true,"snapToGrid":false,"units":"Pixels","dimensionsVisible":true,"dynamicGuides":false,"isCMYKColorPreviewEnabled":false,"undoHistoryDisabled":false,"snapToGuides":true,"drawOnlyUsingPencil":false,"whiteBackground":false,"rulersVisible":true,"isTimeLapseWatermarkDisabled":false},"artboardPaths":["Artboard0.json"],"documentVersion":"unknown"}}
\ No newline at end of file +{"date":644900643.85054696,"appVersion":"4.1.5","drawing":{"modificationDate":644894800.328192,"activeArtboardIndex":0,"settings":{"outlineMode":false,"isolateActiveLayer":false,"snapToEdges":false,"snapToPoints":false,"guidesVisible":true,"snapToGrid":false,"units":"Pixels","dimensionsVisible":true,"dynamicGuides":false,"isCMYKColorPreviewEnabled":false,"undoHistoryDisabled":false,"snapToGuides":true,"drawOnlyUsingPencil":false,"whiteBackground":false,"rulersVisible":true,"isTimeLapseWatermarkDisabled":false},"artboardPaths":["Artboard0.json"],"documentVersion":"unknown"}} diff --git a/docs/images/logo.vectornator/Manifest.json b/docs/images/logo.vectornator/Manifest.json index 0f80b78..8e897a0 100644 --- a/docs/images/logo.vectornator/Manifest.json +++ b/docs/images/logo.vectornator/Manifest.json @@ -1 +1 @@ -{"documentJSONFilename":"Document.json","undoHistoryJSONFilename":"UndoHistory.json","fileFormatVersion":0,"thumbnailImageFilename":"Thumbnail.png"}
\ No newline at end of file +{"documentJSONFilename":"Document.json","undoHistoryJSONFilename":"UndoHistory.json","fileFormatVersion":0,"thumbnailImageFilename":"Thumbnail.png"} diff --git a/docs/images/logo.vectornator/UndoHistory.json b/docs/images/logo.vectornator/UndoHistory.json index ead621d..e9379db 100644 --- a/docs/images/logo.vectornator/UndoHistory.json +++ b/docs/images/logo.vectornator/UndoHistory.json @@ -1 +1 @@ -{"cacheElements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[320,664.0506297595],"reflectionModeOverride":0,"anchorPoint":[320,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[320,580],"nextPoint":[0,0]},{"outPoint":[344.050612449646,595.89893977911322],"reflectionModeOverride":0,"anchorPoint":[337.34381007033926,612.48211711168926],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[327.23039949003834,637.48844227310212],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[360,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[352.025306224823,580],"nextPoint":[0,0]},{"outPoint":[260,580],"reflectionModeOverride":0,"anchorPoint":[280,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":true,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":34,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.67257107604084,316.56452785377144],"reflectionModeOverride":0,"anchorPoint":[221.67257107604084,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257107604084,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.78002147975192,316.58196755365179],"reflectionModeOverride":0,"anchorPoint":[222.50257105935154,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.50257105935154,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.46754319256974,316.15798118973765],"reflectionModeOverride":0,"anchorPoint":[223.27257099819641,316.3245278149393],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.05416436926282,316.49652037792839],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.56257095468965,315.05452779244973],"reflectionModeOverride":0,"anchorPoint":[223.56257095468965,315.65452781629159],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.57458948209043,315.9106673969701],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.50257101191011,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[222.50257101191011,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.18257095945802,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257102859941,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[221.67257102859941,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257102859941,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":7,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[223.39257110465107,318.92452774886726],"reflectionModeOverride":0,"anchorPoint":[223.39257110465107,318.92452774886726],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.39257110465107,318.92452774886726],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.33257116187153,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[222.33257116187153,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.33257116187153,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,314.29453168754219],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.85351610118363,314.2910670771638],"reflectionModeOverride":0,"anchorPoint":[222.64257116425571,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.64257116425571,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.44257068590508,314.46434348341825],"reflectionModeOverride":0,"anchorPoint":[223.26257121864279,314.39453162176318],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.06341253016012,314.32492145971571],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.8867055322342,314.82297152263595],"reflectionModeOverride":0,"anchorPoint":[223.75257125112995,314.69453165532451],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.60854628757448,314.56596123813961],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.15455094852729,315.31612333025106],"reflectionModeOverride":0,"anchorPoint":[224.07257117822328,315.14453165999419],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.99528738469252,314.97566482247669],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.19825083117667,315.9922372888982],"reflectionModeOverride":0,"anchorPoint":[224.19257122020886,315.69453168706491],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.19562660185056,315.50438689028209],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.7338072115908,316.75555373263109],"reflectionModeOverride":0,"anchorPoint":[223.91257119347284,316.51453168318102],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.09914577717603,316.28247388344346],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.19257120773227,317.01453168185151],"reflectionModeOverride":0,"anchorPoint":[223.19257120773227,317.01453168185151],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.48085346525613,316.9312160768892],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.99257120475204,317.08453168214953],"reflectionModeOverride":0,"anchorPoint":[222.99257120475204,317.08453168214953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.99257120475204,317.08453168214953],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.0925712285939,318.97453166784442],"reflectionModeOverride":0,"anchorPoint":[224.0925712285939,318.97453166784442],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.0925712285939,318.97453166784442],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":8,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.88165496610824,312.75214941635204],"reflectionModeOverride":0,"anchorPoint":[222.39257110465107,312.75452767257332],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257110465107,312.75452767257332],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.45378029794426,313.23854645592189],"reflectionModeOverride":0,"anchorPoint":[220.90257124445034,313.04452773196982],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.37529974656684,312.85070177480884],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.35876943156495,314.2208600575953],"reflectionModeOverride":0,"anchorPoint":[219.70257152159786,313.86452769906913],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.04640821090715,313.51691728007791],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.51848057480817,316.05954140545026],"reflectionModeOverride":0,"anchorPoint":[218.89257170245423,315.09452765536599],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.0841152815558,314.63792794299417],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.07885049250976,318.55391401613247],"reflectionModeOverride":0,"anchorPoint":[218.89257166857527,318.09452771720584],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.51848023468915,317.12951359608263],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.04640881460426,319.67213836278347],"reflectionModeOverride":0,"anchorPoint":[219.70257171508007,319.32452783266035],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.35412998721458,318.97193143333453],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.32887552639454,320.75534631866788],"reflectionModeOverride":0,"anchorPoint":[220.90257185775954,320.14452762388811],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.45378073087409,319.9505090873252],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.42747093525361,318.97720769048311],"reflectionModeOverride":0,"anchorPoint":[225.07257197531021,319.32452740981233],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.98375014972524,320.42992729161517],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25666363710593,317.12951355179308],"reflectionModeOverride":0,"anchorPoint":[225.8825720147413,318.09452742555698],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.70369149262439,318.55776181404121],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.69870344883279,314.63387871118448],"reflectionModeOverride":0,"anchorPoint":[225.88257204862026,315.09452736371713],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.25666342066654,316.05954129932087],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.73054756988128,313.51475021332715],"reflectionModeOverride":0,"anchorPoint":[225.07257201185996,313.86452742080394],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.42312528062655,314.21540813724465],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.40302999066401,312.851739523118],"reflectionModeOverride":0,"anchorPoint":[223.87257194075443,313.04452740721484],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.32272320141846,313.23607014436448],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.39257194847221,312.754527354262],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,312.754527354262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.90014945704277,312.7532021443493],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":9,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.79793545229796,321.06845412941806],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,321.06452777387869],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257194847221,321.06452777387869],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.13630955687603,320.47368746402753],"reflectionModeOverride":0,"anchorPoint":[220.66257205810552,320.71452796827054],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.20891937561655,320.94928924957026],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.84008207185138,319.31458146286332],"reflectionModeOverride":0,"anchorPoint":[219.26257159607349,319.71452784560978],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.66107353858615,320.13423302542299],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[217.79637500761001,317.20177600727493],"reflectionModeOverride":0,"anchorPoint":[218.26257166453621,318.30452834140976],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.50031051375157,318.83550383670706],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.71657507260346,313.79480682341318],"reflectionModeOverride":0,"anchorPoint":[218.26257150938892,314.85452883246262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[217.79637484257967,315.95728089190948],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.15964890699888,312.25319776201661],"reflectionModeOverride":0,"anchorPoint":[220.61257125382446,312.48452913388269],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.5567350056233,312.94749659064405],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.93948006388655,312.14028017121217],"reflectionModeOverride":0,"anchorPoint":[222.34257146546875,312.14452894709979],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.74863705025209,312.1374427227563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.61118094730551,312.72639164873863],"reflectionModeOverride":0,"anchorPoint":[224.08257153232208,312.48452915920495],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.53116831317226,312.25589748063879],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.90916737355408,313.88955714821628],"reflectionModeOverride":0,"anchorPoint":[225.49257153005306,313.48452928232075],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.08951592668194,313.06563634089633],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.95876793869749,315.99728178436908],"reflectionModeOverride":0,"anchorPoint":[226.49257128177129,314.89452945023424],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.24808810980261,314.36743571685838],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25070868333646,318.87313890226199],"reflectionModeOverride":0,"anchorPoint":[226.49257117287002,318.34452948727852],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.95876813673391,317.24177716378313],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.65955409863321,320.59420064219239],"reflectionModeOverride":0,"anchorPoint":[225.49257104975428,319.75452948500953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.91146402567489,319.35147381264608],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.34257072457945,321.06452950412722],"reflectionModeOverride":0,"anchorPoint":[222.34257072457945,321.06452950412722],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.52534831488276,321.06588609892162],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":10,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":6,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":1,"s":0,"h":0,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[189.49257363188801,253.97453151588081],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,253.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,253.97453151588081],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[198.97257317412434,244.48453174476265],"reflectionModeOverride":0,"anchorPoint":[198.97257317412434,244.48453174476265],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[198.97257317412434,244.48453174476265],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[189.49257363188801,235.01453147773384],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,235.01453147773384],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,235.01453147773384],"nextPoint":[112.85257138121662,152.97453151588078]}],"closed":true,"reversed":false}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,152.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":16,"smootheningRate":0,"initialPoint":[112.85257138121662,152.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300,640],"reflectionModeOverride":0,"anchorPoint":[300,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,640],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[340,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":36,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.2196979967656,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[270.70254252087443,481.42866614307741],"cornerRadius":0,"prevPoint":[0,1.3634276556741725],"inPoint":[246.45876716860445,474.78563603599895],"nextPoint":[0,1.3634276556741725]},{"outPoint":[308.32019835237998,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.80961189523731,532.13830093176216],"cornerRadius":0,"prevPoint":[9.9345758586960642,16.47175369678871],"inPoint":[307.29902543809465,503.80404406807986],"nextPoint":[9.9345758586960642,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[9.9345758586960642,15.108326041114537],"opacity":1,"blur":0,"isLocked":false,"gid":44,"smootheningRate":0,"initialPoint":[9.9345758586960642,15.108326041114537],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(polygon)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[368.75294547403814,729.44134214300266],"reflectionModeOverride":0,"anchorPoint":[368.75294547403814,729.44134214300266],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75294547403814,729.44134214300266],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884741476317231,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[58.884741476317231,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884741476317231,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884771993693448,192.73377248033557],"reflectionModeOverride":0,"anchorPoint":[58.884771993693448,192.73377248033557],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884771993693448,192.73377248033557],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[368.75296538097359,13.831330639116459],"reflectionModeOverride":0,"anchorPoint":[368.75296538097359,13.831330639116459],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75296538097359,13.831330639116459],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62124178681802,192.73392506721666],"reflectionModeOverride":0,"anchorPoint":[678.62124178681802,192.73392506721666],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62124178681802,192.73392506721666],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62118075206558,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[678.62118075206558,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62118075206558,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":0.3074892778331435},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":18.646402359008789,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[368.75296111419152,729.44134214300266],"opacity":1,"blur":0,"isLocked":false,"gid":57,"smootheningRate":0,"initialPoint":[368.75296111419152,371.63633639105956],"creationPoints":[],"name":"(polygon)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[340,700],"reflectionModeOverride":0,"anchorPoint":[340,660],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[340,620],"nextPoint":[0,0]},{"outPoint":[340,600],"reflectionModeOverride":0,"anchorPoint":[380,600],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[420,600],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":35,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[271.39702488697174,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[345.33038784778722,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,547.81167390718997],"reflectionModeOverride":0,"anchorPoint":[308.36370636737951,547.81167390718997],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,547.81167390718997],"nextPoint":[86.563617484933189,-170.37985441312253]}],"closed":true,"reversed":true}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[579.21777967228218,-165.50118646031109],"opacity":1,"blur":0,"isLocked":false,"gid":37,"smootheningRate":0,"initialPoint":[579.21777967228218,-165.50118646031109],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[13.974435927889729,160.80268978822852],"reflectionModeOverride":0,"anchorPoint":[13.974435927889729,160.80268978822852],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[13.974435927889729,160.80268978822852],"nextPoint":[0,0]},{"outPoint":[144.25556161242727,291.78142890790133],"reflectionModeOverride":0,"anchorPoint":[144.25556161242727,291.78142890790133],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[144.25556161242727,291.78142890790133],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":28,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[323.38754763944246,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[344.90470311533363,481.42866614307741],"cornerRadius":0,"prevPoint":[615.60724563620806,1.3634276556741725],"inPoint":[369.14847846760358,474.78563603599895],"nextPoint":[615.60724563620806,1.3634276556741725]},{"outPoint":[307.28704728382809,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.79763374097075,532.13830093176216],"cornerRadius":0,"prevPoint":[605.67266977751206,16.47175369678871],"inPoint":[308.30822019811342,503.80404406807986],"nextPoint":[605.67266977751206,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[605.67266977751206,16.47175369678871],"opacity":1,"blur":0,"isLocked":false,"gid":45,"smootheningRate":0,"initialPoint":[605.67266977751206,16.47175369678871],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[307.98765662154068,497.22766759942954],"reflectionModeOverride":0,"anchorPoint":[307.98765662154068,526.40806463936553],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[307.98765662154068,555.58846167930153],"nextPoint":[0,0]},{"outPoint":[347.07124677007465,468.5633046399771],"reflectionModeOverride":0,"anchorPoint":[331.56894097295356,478.9361833057668],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[316.06663517583246,489.3090619715565],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":33,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.7690644381571,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.7690644381571,667.02363476289884],"cornerRadius":0,"prevPoint":[72.193980608055426,108.16792868331186],"inPoint":[368.26333755475412,638.68961321030247],"nextPoint":[72.193980608055426,108.16792868331186]},{"outPoint":[367.82059343747073,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.82059343747073,616.38299074283532],"cornerRadius":0,"prevPoint":[55.02340366515682,81.837967291336099],"inPoint":[367.82059343747073,616.38299074283532],"nextPoint":[55.02340366515682,81.837967291336099]},{"outPoint":[383.13716259159514,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[403.6767241609266,616.33647011725679],"cornerRadius":0,"prevPoint":[70.109671854888575,134.1591558035534],"inPoint":[403.6767241609266,616.33647011725679],"nextPoint":[70.109671854888575,134.1591558035534]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[72.357622652088821,121.79167358010636],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[72.357622652088821,121.79167358010636],"creationPoints":[],"name":"(curve)"}]}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-2.4740749822735211,26.734403557988003],"opacity":1,"blur":0,"isLocked":false,"gid":46,"smootheningRate":0,"initialPoint":[-2.4740749822735211,26.734403557988003],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-17.790763092914176,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[-17.790763092914176,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-17.790763092914176,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-118.14742861878335,355.38893101990755],"reflectionModeOverride":0,"anchorPoint":[-118.14742861878335,355.38893101990755],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-118.14742861878335,355.38893101990755],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-89.082653539453304,326.03057263672883],"reflectionModeOverride":0,"anchorPoint":[-89.082653539453304,326.03057263672883],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-89.082653539453304,326.03057263672883],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,406.22781863362536],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,406.22781863362536],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,406.22781863362536],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,214.66453204993843],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,214.66453204993843],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,214.66453204993843],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,359.98840044701058],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,359.98840044701058],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,359.98840044701058],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[40.289832016581158,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[40.289832016581158,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[40.289832016581158,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,551.89420905136228],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,551.89420905136228],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,551.89420905136228],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,697.21808678120522],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,697.21808678120522],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,697.21808678120522],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,505.65480953028907],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,505.65480953028907],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,505.65480953028907],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-88.78907023560464,585.60738760822585],"reflectionModeOverride":0,"anchorPoint":[-88.78907023560464,585.60738760822585],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-88.78907023560464,585.60738760822585],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-117.85384531493469,556.24902922504714],"reflectionModeOverride":0,"anchorPoint":[-117.85384531493469,556.24902922504714],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-117.85384531493469,556.24902922504714],"nextPoint":[-343.22817622315347,103.6410041510419]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-343.22817622315347,103.6410041510419],"opacity":1,"blur":0,"isLocked":false,"gid":13,"smootheningRate":0,"initialPoint":[-343.22817622315347,103.6410041510419],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-87.50742636811205,292.97453151588081],"reflectionModeOverride":0,"anchorPoint":[-87.50742636811205,292.97453151588081],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.50742636811205,292.97453151588081],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-78.027426825875665,283.48453174476265],"reflectionModeOverride":0,"anchorPoint":[-78.027426825875665,283.48453174476265],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-78.027426825875665,283.48453174476265],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-87.507426368111936,274.01453147773384],"reflectionModeOverride":0,"anchorPoint":[-87.507426368111936,274.01453147773384],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.507426368111936,274.01453147773384],"nextPoint":[-164.14742861878338,191.97453151588081]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588081],"opacity":1,"blur":0,"isLocked":false,"gid":14,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588081],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":11,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588078],"creationPoints":[],"name":"(curve)"}],"cacheLayers":[],"cacheArtboardPaths":[],"undoStack":[[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11,6]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -29.209948435277369, -232.41660739687057]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14,15]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.01450315572447696,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93007444840156717,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93451308072143913,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93910701763028559,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94055809677531532,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94999337125602745,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95297621548706413,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95790068402842177,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.011044777045815676,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96266052064980523,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0088583736096398309,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96911575815444173,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0077646546444650423,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97217928832645584,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0032726223185911016,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98016728703982747,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0010851843882415254,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98662352066719217,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99307006473711057,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.20437110010339629, -0, -0, 0.20437110010339629, 126.38769661378409, 170.79330548171168]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[13]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":2,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":186.05023255601679,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":558.85414169234264,\"OutPoint_X\":186.05023255601679,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":558.85414169234264},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":1,\"NodePoints\":{\"inPoint_X\":235.46249740746129,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":509.44413050490067,\"OutPoint_X\":136.6379677045723,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":608.26415287978466},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.03408561318607653,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.029926881951800856,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.021585238181938561,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.012961911164688478,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.011895648503707629,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.007872629590133573,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.005957393322960804,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-4.3711390001862419e-08, -0.99999999999999922, 0.99999999999999922, -4.3711390001862419e-08, -173.29768953296772, 744.95852622656867]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 8.2190355245806472, 176.90598115888827]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.55538688884063114, -0, 182.49084631957811]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 150.32620239929324, 36.081797695525552]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12249755187863798, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.64978701032960984, -0, -0, 0.64978701032960984, 52.531948450558538, 142.05406077476505]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.171276915352394, 196.69205141995428]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.65616231221472443, -0, -0, 0.65616231221472443, 51.425334356867019, 141.12771095949628]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.53221494113933, 129.60740108742715]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.17127691535239, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.09503537570964, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 208.92985571850454]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 130.84123782858848]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 75]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 4.5418914390142788, -306.56258907512347]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.96223302635797447, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0392493219135692, -0, -0, 1.0392493219135692, 0, -2.9436991435176969]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 15.458108560985721, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 26.562589075123469]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 50]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":22}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 9.881426012744555, 0.91199734232554874]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -77.745954011856824, 28.270796276850916]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 67.864527999112269, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.1538461432654479, -0, -0, 1, -1.5384614326544779, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -136.98574299882625, 7.8612196478959504]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -53.141264363887032, -43.604121250556744]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -70.052055171480333, -2.5049969673447094]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 219.77475934149726, 163.57319520938375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.6846814845770105, -0, -0, 0.6846814845770105, -0.080018108436871196, 3.1890182924268196]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -85.587957051898314, -21.453286888159028]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":142.86303913891427,\"AnchorPoint_Y\":297.21030751597522,\"inPoint_Y\":297.21030751597522,\"OutPoint_X\":142.86303913891427,\"AnchorPoint_X\":142.86303913891427,\"OutPoint_Y\":297.21030751597522},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -258.8309537310077, 14.790477788784642]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.98660023945785613, -0, -0, 1.0127745463822073, 7.5897658853896965, -3.7483052243093384]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":307.98765662154068,\"AnchorPoint_Y\":526.40806463936553,\"inPoint_Y\":555.58846167930153,\"OutPoint_X\":307.98765662154068,\"AnchorPoint_X\":307.98765662154068,\"OutPoint_Y\":497.22766759942954},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":9223372036854775807}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":580,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":680.00002878904479},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":660,\"inPoint_Y\":620,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":700},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 20, 20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 640, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 39, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":220,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 60, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{540, 0}\",\"VNPrevPointForRadiusKey\":\"{540, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":300,\"AnchorPoint_X\":280,\"OutPoint_Y\":600},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[],"methodSignature":"reversePathDirection","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":600,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":240,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -60, 100]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -8.3360061810856791, -0.18596575427056905]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12624915594960839, -0.84513498021431133]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0820554726071352, -0, -0, 1, -28.572516179113212, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 3.7693360211719664, 0.58007166688469169]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.8911271920692454, -0, 58.840421083655528]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0.018101184397323777, -6.1834757589472247]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.5616683932951787, -14.721591768371582]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.4335340527209723, 13.544422687729025]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":43}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.10440918421369,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":598.11903819000008},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":366.0442444460744,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.21462016330969},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":337.32713106884489,\"AnchorPoint_Y\":505.14299981511687,\"inPoint_Y\":505.14299981511687,\"OutPoint_X\":337.32713106884489,\"AnchorPoint_X\":337.32713106884489,\"OutPoint_Y\":505.14299981511687},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":282.38947067174871,\"AnchorPoint_Y\":502.68576551434728,\"inPoint_Y\":502.68576551434728,\"OutPoint_X\":282.38947067174871,\"AnchorPoint_X\":282.38947067174871,\"OutPoint_Y\":502.68576551434728},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"VNPrevPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":303.99512941310888,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":487.18557523329599,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":45}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 618.5121544161118, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -17.095091220096265, 18.636572344325828]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 2.4740749822735211, -26.734403557988003]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[46]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":50}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":50}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":323.12940602919167,\"AnchorPoint_Y\":534.54856008655031,\"inPoint_Y\":534.54856008655031,\"OutPoint_X\":323.12940602919167,\"AnchorPoint_X\":323.12940602919167,\"OutPoint_Y\":534.54856008655031},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[47,48]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":46},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13822251558303833,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -2.1531569701203352, 25.991399082341331]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":308.00218177519929,\"AnchorPoint_Y\":532.86800447564667,\"inPoint_Y\":504.53379546064855,\"OutPoint_X\":307.49158198784681,\"AnchorPoint_X\":307.49158198784681,\"OutPoint_Y\":532.86800447564667},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.54481307650724,\"AnchorPoint_Y\":482.22702540901258,\"inPoint_Y\":482.22702540901258,\"OutPoint_X\":307.54481307650724,\"AnchorPoint_X\":307.54481307650724,\"OutPoint_Y\":482.22702540901258},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"VNPrevPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":344.58533198784681,\"AnchorPoint_Y\":482.18050447564667,\"inPoint_Y\":482.18050447564667,\"OutPoint_X\":323.06817538141632,\"AnchorPoint_X\":344.58533198784681,\"OutPoint_Y\":488.07641427863712},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.34003453224391933]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.1954377384541317]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 692.07691397569363, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 57.024957634971202, 19.792602829855639]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":307.04977456552314,\"AnchorPoint_Y\":532.53992937509292,\"inPoint_Y\":504.20572036009486,\"OutPoint_X\":307.56037435287561,\"AnchorPoint_X\":307.56037435287561,\"OutPoint_Y\":532.53992937509292},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"VNPrevPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.50714326421519,\"AnchorPoint_Y\":481.89895030845889,\"inPoint_Y\":481.89895030845889,\"OutPoint_X\":307.50714326421519,\"AnchorPoint_X\":307.50714326421519,\"OutPoint_Y\":481.89895030845889},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"VNPrevPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":270.46662435287561,\"AnchorPoint_Y\":481.85242937509298,\"inPoint_Y\":481.85242937509298,\"OutPoint_X\":291.68464211968808,\"AnchorPoint_X\":270.46662435287561,\"OutPoint_Y\":491.96348695323337},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"VNPrevPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":55},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0330248439984473, -0, -0, 0.99999999999999989, -11.379876833051835, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[55]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":56},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":55}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":57}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -290.115478515625, 62.302734375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.89467432944640213,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.90097236067321962,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.031593452065677957,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.91739226661025597,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.019517801575741518,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93616537665754818,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0091232041181144082,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94328004740819738,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0018620895127118644,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95006183205443251,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95531556443573817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9587083584477003,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96669617604784863,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96977202191904677,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97283138648692502,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97731792325435829,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9818318081185089,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9863455118694362,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99094089768406901,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99553628349870182,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -138.240478515625, -358.288818359375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10.2197265625, 7.887451171875]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.73681640625, -1.3956298828125]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96954748896640086,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96362693977790437,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.95841712343394081,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.954131593465262,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.93934857275056949,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.83357840083997725,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.80253639308086555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.76741239856207288,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.7565612097807517,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.73508462058656032,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.69529915290432798,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.68285743522209563,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.66214385499715267,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.64970324957289294,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.63305608449601369,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.62061547907175396,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.60169597095671978,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.59555519468963558,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5870419721668565,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.57588046341116172,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.56733832218109337,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5457271497722096,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.51220146996013671,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.45939702270785876,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.41278562784738043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.38011749893223234,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.35006206399487472,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.33336373505125283,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31040673049544421,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29577720138097952,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.27499243664578588,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.25639993237471526,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24782776017938496,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23669294561503418,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22722206897779043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22344372864464693,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21991675861332574,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21874110193621868,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2197966347522779,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23113499252562641,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24180488325740318,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2529452591116173,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.28146132901480636,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29042946504840544,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29303437322038722,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30319262528473806,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31050016016514809,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31284924900341687,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31402379342255127,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31192273811218679,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31087054207004555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30975717183940776,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.3074892778331435,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0000066161593433, -0, -0, 1.0000066161593433, -0.00084115369224984499, -0.0015278888873493538]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 106.25150911801597, 215.10157288799655]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1024, 1024}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1018.1834573966, 1022.316502396953}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1008.5644489681886, 1015.9218488910001}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1003.3383988868811, 1012.188740268123}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {959.56182404754463, 968.37466824766148}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {930.99873639690077, 938.04865943448704}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {883.88375480634159, 898.86742705675647}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {851.39277451811745, 876.13749149913815}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {807.42735481491377, 847.80421878677203}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {780.8131139470363, 833.66058405713125}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {758.18676120653936, 823.6793880913026}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {745.42543866806363, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.45307691385142, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.65313908147255}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {735.7456294412641, 820.1359296408807}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {734.20024226104533, 819.105537302637}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {730.82720724054207, 815.73250228213374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {722.51078545309133, 807.41391622785272}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {715.22999050970566, 800.13291995732015}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {710.02528110597814, 794.3195482564854}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {708.99086222479468, 792.76802059828378}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {705.03881032957679, 786.83843280185465}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {703.71729893680003, 785.04591654871797}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {702.86608775936838, 784.19455437592615}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {701.16326275021129, 782.49188036212945}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {700.77188277648224, 782.10024872946656}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.97704320020512, 781.30550981676288}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.12865060283116, 780.87985389626033}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {698.70304501411556, 780.45414764397106}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {695.54100084364723, 776.8204946317021}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {693.70127337454369, 774.98056583545167}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {691.86235121402819, 772.67134345959084}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.97672115541332, 769.84279770809485}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.03491276184081, 768.90119064166925}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {686.67817717929256, 766.07264489017348}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {683.90469440249581, 763.76603976722345}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {682.53607245732564, 762.86897633206718}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {681.17026909221318, 762.39756881741346}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {678.95083862390538, 761.54620664462163}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {676.30580256688177, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.07408308165805, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.28025820206608, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {671.48603066818009, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.54341696601955, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.17600298373122, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.39103649860635, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.44842279644581, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.02281720772999, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.226367014277, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {663.57830298909857, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.7291050831368, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.92701772956843, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.54691207607016, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.69429082217562}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.30406847954168}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.91817467056762}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.52795232793369}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.12605301077474}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.73814593033103}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.96107347477482}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.97694686201953, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 757.18274272455005}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 756.35493582795493}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.94518475206382}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.5517915068649}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.78614436689986}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.3960730196261}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.00237778370661}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.60918586565458}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.21065877820774}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.81701387407497}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.41430924832821}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 749.69714774089903}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 748.93285955917622}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.99004452986878}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.13868235707696}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.41906127678772, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.65522608114543, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.27149653900142, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.88132452815421, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.48793128295529, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.69429966956, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.30453031300658, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.68503862079888, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.0695734715307, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.47263041978476, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.86843959074758, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.25498771294906, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.63670398362319, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.02566803158879, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.83158060094979, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {664.08383545516563, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.7862578100287, 746.7378903392264}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.06307457617595, 747.16359659151567}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.70851328749336, 748.01490843252077}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.98533005364084, 748.44061468481004}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.66399580516031, 748.84437627807847}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.94081257130756, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.19387273411121, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.58525270784025, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.20474440004818, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.93235683113426, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.1381292972485, 747.52296554887494}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.87500277709569, 747.09725929658589}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.47194582884163, 746.2471050866759}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.64368594616599, 745.41864387685314}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 745.02308636482417}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":330.67709661876142,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":247.83495910094825},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":214.25650324349817,\"inPoint_Y\":214.25650324349817,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":214.25650324349817},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":247.83495910094825,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":330.67709661876142},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":364.25550551101242,\"inPoint_Y\":364.25550551101242,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":364.25550551101242},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":580.67544258986891,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":497.83330507205574},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":464.25484921460566,\"inPoint_Y\":464.25484921460566,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":464.25484921460566},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":497.83330507205574,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":580.67544258986891},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":614.2538514821199,\"inPoint_Y\":614.2538514821199,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":614.2538514821199},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":97.57351080287799,\"AnchorPoint_Y\":309.16721333385931,\"inPoint_Y\":309.16721333385931,\"OutPoint_X\":97.57351080287799,\"AnchorPoint_X\":97.57351080287799,\"OutPoint_Y\":309.16721333385931},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":221.36797043606543,\"AnchorPoint_Y\":443.90288627635027,\"inPoint_Y\":443.90288627635027,\"OutPoint_X\":221.36797043606543,\"AnchorPoint_X\":221.36797043606543,\"OutPoint_Y\":443.90288627635027},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":361.08361414401247,\"AnchorPoint_Y\":309.6035637643763,\"inPoint_Y\":309.6035637643763,\"OutPoint_X\":361.08361414401247,\"AnchorPoint_X\":361.08361414401247,\"OutPoint_Y\":309.6035637643763},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":377.82365759764593,\"AnchorPoint_Y\":307.07623767819223,\"inPoint_Y\":307.07623767819223,\"OutPoint_X\":377.82365759764593,\"AnchorPoint_X\":377.82365759764593,\"OutPoint_Y\":307.07623767819223},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":503.2994629767461,\"AnchorPoint_Y\":440.11243356580462,\"inPoint_Y\":440.11243356580462,\"OutPoint_X\":503.2994629767461,\"AnchorPoint_X\":503.2994629767461,\"OutPoint_Y\":440.11243356580462},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":644.91269001662772,\"AnchorPoint_Y\":307.50708423933065,\"inPoint_Y\":307.50708423933065,\"OutPoint_X\":644.91269001662772,\"AnchorPoint_X\":644.91269001662772,\"OutPoint_Y\":307.50708423933065},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":516.45695350382334,\"AnchorPoint_Y\":154.80813254227905,\"inPoint_Y\":154.80813254227905,\"OutPoint_X\":516.45695350382334,\"AnchorPoint_X\":516.45695350382334,\"OutPoint_Y\":154.80813254227905},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":223.97172601151692,\"AnchorPoint_Y\":448.88181029919406,\"inPoint_Y\":448.88181029919406,\"OutPoint_X\":223.97172601151692,\"AnchorPoint_X\":223.97172601151692,\"OutPoint_Y\":448.88181029919406},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":78.252109034647219,\"AnchorPoint_Y\":300.1041732651538,\"inPoint_Y\":300.1041732651538,\"OutPoint_X\":78.252109034647219,\"AnchorPoint_X\":78.252109034647219,\"OutPoint_Y\":300.1041732651538},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":648.07861404640448,\"AnchorPoint_Y\":302.9355554780409,\"inPoint_Y\":302.9355554780409,\"OutPoint_X\":648.07861404640448,\"AnchorPoint_X\":648.07861404640448,\"OutPoint_Y\":302.9355554780409},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":506.17105679039673,\"AnchorPoint_Y\":444.7847840681311,\"inPoint_Y\":444.7847840681311,\"OutPoint_X\":506.17105679039673,\"AnchorPoint_X\":506.17105679039673,\"OutPoint_Y\":444.7847840681311},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":217.51314851045686,\"AnchorPoint_Y\":156.11368443580113,\"inPoint_Y\":156.11368443580113,\"OutPoint_X\":217.51314851045686,\"AnchorPoint_X\":217.51314851045686,\"OutPoint_Y\":156.11368443580113},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":362.67570746371234,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.18143434711533,\"AnchorPoint_X\":362.18143434711533,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.23296334642896,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.23296334642896,\"AnchorPoint_X\":362.23296334642896,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":398.08909406988482,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":377.54953250055337,\"AnchorPoint_X\":398.08909406988482,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":361.7537539269963,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.24802704359331,\"AnchorPoint_X\":362.24802704359331,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.19649804427968,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.19649804427968,\"AnchorPoint_X\":362.19649804427968,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":326.34036732082382,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":346.87992889015482,\"AnchorPoint_X\":326.34036732082382,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":\"Mac App icon\"}","argumentGID":0}],"methodSignature":"title","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":57},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 51.701018329683393, 116.18082292683278]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {740, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {736.35253901673036, 740.35218585345365}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {726.62032261171248, 732.07890244371663}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {706.29497285008392, 715.91557216276851}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {691.48129050959142, 702.64569380957585}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {676.51521301916046, 688.7168285032127}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.9666932377902, 683.82682021816697}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.51490451806353, 675.17444423582754}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.05376131978483, 661.73639796317264}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {652.798107458392, 653.69283161018325}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {646.24074717873964, 645.29847372318682}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {639.28068335784974, 635.32950515648088}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.62597526532704, 630.02008897143548}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.24953610331818, 624.67241338007113}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 622.6021329778589}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 621.53096597223009}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 620.05992231684661}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 617.99269367248519}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 616.51850188795038}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 616.24683119169595}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 615.70516023506332}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 615.17627453763725}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 614.91966988803563}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 614.41860337270202}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 614.17012603611374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 613.92274090759827}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.49213479057289, 613.12141354351331}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.76223142225149, 612.8513490356014}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.57611917917507, 612.03746127867794}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.11913933401547, 611.49444112383765}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 610.95251317707016}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 609.88031821090215}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 609.63566360256891}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 609.14548704419769}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 605.7312446082625}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 599.34404392161696}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 593.09060660013677}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 589.48773340525565}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 588.67320317299516}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 588.41560268662124}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 588.17014498411686}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 587.37090566487711}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 585.89687449917653}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.75570389792438, 583.82575887902624}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.35654683309554, 582.08108497761054}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 581.53800057523642}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 581.26632987898211}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 580.49002692927365}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 579.01760195191559}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 577.54289618711107}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 576.39787664149299}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 574.98324232069888}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.49819978285086, 574.14796013505304}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.68559697660157, 573.33526095750301}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.54147689645515, 572.22030925765694}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.72784612966643, 571.9486064376357}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {633.91472934314743, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {632.17002331796471, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {630.76017543843136, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {630.21689829345632, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {629.95939417838304, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {629.16606563224377, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.41930368598264, 572.55198715038807}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {626.22070169725794, 573.15125602098351}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {624.65229090455068, 574.07519980313668}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.58506226018949, 575.54563310695005}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.31316669756711, 575.81730380320448}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.51598329940589, 576.35817166566585}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.75935295706495, 576.11476988423976}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.01171726944222, 576.11476988423976}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.79887805234603, 575.59915130252296}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {623.61379376980881, 574.78423558506029}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.35849979499153, 574.21346049566114}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {626.4990220132504, 573.64088647531833}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 573.36995462570155}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 573.12317984875619}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 572.87576259647392}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 572.62320554149551}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 572.10858279655099}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 571.85628273170744}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {626.48437357556668, 571.58695707043307}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.96371156245436, 571.06635930485447}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.71160424021173, 571.06635930485447}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.46360876012614, 571.06635930485447}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.21124444774887, 570.81418773507824}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {624.70805776380325, 570.81418773507824}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {624.18174196772497, 570.28806468160133}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {623.92449484278654, 570.01636186157998}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {623.11163504640217, 569.74465904155875}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.57632459560614, 569.20931646699569}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.31933446080234, 569.20931646699569}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.04743889817996, 568.93899496894903}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.04743889817996, 568.68393226015633}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.80252729971198, 568.68393226015633}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.80252729971198, 570.51884182265508}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {600, 570.51884182265508}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 15.687962779094846, 1.6061883343354566e-05]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}]],"isGroupingUndo":false,"currentUndoGroup":[],"currentRedoGroup":[],"isGroupingRedo":false,"redoStack":[],"nextGID":57}
\ No newline at end of file +{"cacheElements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[320,664.0506297595],"reflectionModeOverride":0,"anchorPoint":[320,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[320,580],"nextPoint":[0,0]},{"outPoint":[344.050612449646,595.89893977911322],"reflectionModeOverride":0,"anchorPoint":[337.34381007033926,612.48211711168926],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[327.23039949003834,637.48844227310212],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[360,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[352.025306224823,580],"nextPoint":[0,0]},{"outPoint":[260,580],"reflectionModeOverride":0,"anchorPoint":[280,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":true,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":34,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.67257107604084,316.56452785377144],"reflectionModeOverride":0,"anchorPoint":[221.67257107604084,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257107604084,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.78002147975192,316.58196755365179],"reflectionModeOverride":0,"anchorPoint":[222.50257105935154,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.50257105935154,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.46754319256974,316.15798118973765],"reflectionModeOverride":0,"anchorPoint":[223.27257099819641,316.3245278149393],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.05416436926282,316.49652037792839],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.56257095468965,315.05452779244973],"reflectionModeOverride":0,"anchorPoint":[223.56257095468965,315.65452781629159],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.57458948209043,315.9106673969701],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.50257101191011,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[222.50257101191011,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.18257095945802,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257102859941,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[221.67257102859941,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257102859941,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":7,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[223.39257110465107,318.92452774886726],"reflectionModeOverride":0,"anchorPoint":[223.39257110465107,318.92452774886726],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.39257110465107,318.92452774886726],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.33257116187153,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[222.33257116187153,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.33257116187153,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,314.29453168754219],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.85351610118363,314.2910670771638],"reflectionModeOverride":0,"anchorPoint":[222.64257116425571,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.64257116425571,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.44257068590508,314.46434348341825],"reflectionModeOverride":0,"anchorPoint":[223.26257121864279,314.39453162176318],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.06341253016012,314.32492145971571],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.8867055322342,314.82297152263595],"reflectionModeOverride":0,"anchorPoint":[223.75257125112995,314.69453165532451],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.60854628757448,314.56596123813961],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.15455094852729,315.31612333025106],"reflectionModeOverride":0,"anchorPoint":[224.07257117822328,315.14453165999419],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.99528738469252,314.97566482247669],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.19825083117667,315.9922372888982],"reflectionModeOverride":0,"anchorPoint":[224.19257122020886,315.69453168706491],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.19562660185056,315.50438689028209],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.7338072115908,316.75555373263109],"reflectionModeOverride":0,"anchorPoint":[223.91257119347284,316.51453168318102],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.09914577717603,316.28247388344346],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.19257120773227,317.01453168185151],"reflectionModeOverride":0,"anchorPoint":[223.19257120773227,317.01453168185151],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.48085346525613,316.9312160768892],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.99257120475204,317.08453168214953],"reflectionModeOverride":0,"anchorPoint":[222.99257120475204,317.08453168214953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.99257120475204,317.08453168214953],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.0925712285939,318.97453166784442],"reflectionModeOverride":0,"anchorPoint":[224.0925712285939,318.97453166784442],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.0925712285939,318.97453166784442],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":8,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.88165496610824,312.75214941635204],"reflectionModeOverride":0,"anchorPoint":[222.39257110465107,312.75452767257332],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257110465107,312.75452767257332],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.45378029794426,313.23854645592189],"reflectionModeOverride":0,"anchorPoint":[220.90257124445034,313.04452773196982],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.37529974656684,312.85070177480884],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.35876943156495,314.2208600575953],"reflectionModeOverride":0,"anchorPoint":[219.70257152159786,313.86452769906913],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.04640821090715,313.51691728007791],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.51848057480817,316.05954140545026],"reflectionModeOverride":0,"anchorPoint":[218.89257170245423,315.09452765536599],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.0841152815558,314.63792794299417],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.07885049250976,318.55391401613247],"reflectionModeOverride":0,"anchorPoint":[218.89257166857527,318.09452771720584],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.51848023468915,317.12951359608263],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.04640881460426,319.67213836278347],"reflectionModeOverride":0,"anchorPoint":[219.70257171508007,319.32452783266035],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.35412998721458,318.97193143333453],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.32887552639454,320.75534631866788],"reflectionModeOverride":0,"anchorPoint":[220.90257185775954,320.14452762388811],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.45378073087409,319.9505090873252],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.42747093525361,318.97720769048311],"reflectionModeOverride":0,"anchorPoint":[225.07257197531021,319.32452740981233],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.98375014972524,320.42992729161517],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25666363710593,317.12951355179308],"reflectionModeOverride":0,"anchorPoint":[225.8825720147413,318.09452742555698],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.70369149262439,318.55776181404121],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.69870344883279,314.63387871118448],"reflectionModeOverride":0,"anchorPoint":[225.88257204862026,315.09452736371713],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.25666342066654,316.05954129932087],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.73054756988128,313.51475021332715],"reflectionModeOverride":0,"anchorPoint":[225.07257201185996,313.86452742080394],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.42312528062655,314.21540813724465],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.40302999066401,312.851739523118],"reflectionModeOverride":0,"anchorPoint":[223.87257194075443,313.04452740721484],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.32272320141846,313.23607014436448],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.39257194847221,312.754527354262],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,312.754527354262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.90014945704277,312.7532021443493],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":9,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.79793545229796,321.06845412941806],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,321.06452777387869],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257194847221,321.06452777387869],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.13630955687603,320.47368746402753],"reflectionModeOverride":0,"anchorPoint":[220.66257205810552,320.71452796827054],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.20891937561655,320.94928924957026],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.84008207185138,319.31458146286332],"reflectionModeOverride":0,"anchorPoint":[219.26257159607349,319.71452784560978],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.66107353858615,320.13423302542299],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[217.79637500761001,317.20177600727493],"reflectionModeOverride":0,"anchorPoint":[218.26257166453621,318.30452834140976],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.50031051375157,318.83550383670706],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.71657507260346,313.79480682341318],"reflectionModeOverride":0,"anchorPoint":[218.26257150938892,314.85452883246262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[217.79637484257967,315.95728089190948],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.15964890699888,312.25319776201661],"reflectionModeOverride":0,"anchorPoint":[220.61257125382446,312.48452913388269],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.5567350056233,312.94749659064405],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.93948006388655,312.14028017121217],"reflectionModeOverride":0,"anchorPoint":[222.34257146546875,312.14452894709979],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.74863705025209,312.1374427227563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.61118094730551,312.72639164873863],"reflectionModeOverride":0,"anchorPoint":[224.08257153232208,312.48452915920495],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.53116831317226,312.25589748063879],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.90916737355408,313.88955714821628],"reflectionModeOverride":0,"anchorPoint":[225.49257153005306,313.48452928232075],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.08951592668194,313.06563634089633],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.95876793869749,315.99728178436908],"reflectionModeOverride":0,"anchorPoint":[226.49257128177129,314.89452945023424],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.24808810980261,314.36743571685838],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25070868333646,318.87313890226199],"reflectionModeOverride":0,"anchorPoint":[226.49257117287002,318.34452948727852],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.95876813673391,317.24177716378313],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.65955409863321,320.59420064219239],"reflectionModeOverride":0,"anchorPoint":[225.49257104975428,319.75452948500953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.91146402567489,319.35147381264608],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.34257072457945,321.06452950412722],"reflectionModeOverride":0,"anchorPoint":[222.34257072457945,321.06452950412722],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.52534831488276,321.06588609892162],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":10,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":6,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":1,"s":0,"h":0,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[189.49257363188801,253.97453151588081],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,253.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,253.97453151588081],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[198.97257317412434,244.48453174476265],"reflectionModeOverride":0,"anchorPoint":[198.97257317412434,244.48453174476265],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[198.97257317412434,244.48453174476265],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[189.49257363188801,235.01453147773384],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,235.01453147773384],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,235.01453147773384],"nextPoint":[112.85257138121662,152.97453151588078]}],"closed":true,"reversed":false}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,152.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":16,"smootheningRate":0,"initialPoint":[112.85257138121662,152.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300,640],"reflectionModeOverride":0,"anchorPoint":[300,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,640],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[340,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":36,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.2196979967656,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[270.70254252087443,481.42866614307741],"cornerRadius":0,"prevPoint":[0,1.3634276556741725],"inPoint":[246.45876716860445,474.78563603599895],"nextPoint":[0,1.3634276556741725]},{"outPoint":[308.32019835237998,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.80961189523731,532.13830093176216],"cornerRadius":0,"prevPoint":[9.9345758586960642,16.47175369678871],"inPoint":[307.29902543809465,503.80404406807986],"nextPoint":[9.9345758586960642,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[9.9345758586960642,15.108326041114537],"opacity":1,"blur":0,"isLocked":false,"gid":44,"smootheningRate":0,"initialPoint":[9.9345758586960642,15.108326041114537],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(polygon)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[368.75294547403814,729.44134214300266],"reflectionModeOverride":0,"anchorPoint":[368.75294547403814,729.44134214300266],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75294547403814,729.44134214300266],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884741476317231,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[58.884741476317231,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884741476317231,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884771993693448,192.73377248033557],"reflectionModeOverride":0,"anchorPoint":[58.884771993693448,192.73377248033557],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884771993693448,192.73377248033557],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[368.75296538097359,13.831330639116459],"reflectionModeOverride":0,"anchorPoint":[368.75296538097359,13.831330639116459],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75296538097359,13.831330639116459],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62124178681802,192.73392506721666],"reflectionModeOverride":0,"anchorPoint":[678.62124178681802,192.73392506721666],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62124178681802,192.73392506721666],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62118075206558,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[678.62118075206558,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62118075206558,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":0.3074892778331435},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":18.646402359008789,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[368.75296111419152,729.44134214300266],"opacity":1,"blur":0,"isLocked":false,"gid":57,"smootheningRate":0,"initialPoint":[368.75296111419152,371.63633639105956],"creationPoints":[],"name":"(polygon)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[340,700],"reflectionModeOverride":0,"anchorPoint":[340,660],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[340,620],"nextPoint":[0,0]},{"outPoint":[340,600],"reflectionModeOverride":0,"anchorPoint":[380,600],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[420,600],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":35,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[271.39702488697174,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[345.33038784778722,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,547.81167390718997],"reflectionModeOverride":0,"anchorPoint":[308.36370636737951,547.81167390718997],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,547.81167390718997],"nextPoint":[86.563617484933189,-170.37985441312253]}],"closed":true,"reversed":true}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[579.21777967228218,-165.50118646031109],"opacity":1,"blur":0,"isLocked":false,"gid":37,"smootheningRate":0,"initialPoint":[579.21777967228218,-165.50118646031109],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[13.974435927889729,160.80268978822852],"reflectionModeOverride":0,"anchorPoint":[13.974435927889729,160.80268978822852],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[13.974435927889729,160.80268978822852],"nextPoint":[0,0]},{"outPoint":[144.25556161242727,291.78142890790133],"reflectionModeOverride":0,"anchorPoint":[144.25556161242727,291.78142890790133],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[144.25556161242727,291.78142890790133],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":28,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[323.38754763944246,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[344.90470311533363,481.42866614307741],"cornerRadius":0,"prevPoint":[615.60724563620806,1.3634276556741725],"inPoint":[369.14847846760358,474.78563603599895],"nextPoint":[615.60724563620806,1.3634276556741725]},{"outPoint":[307.28704728382809,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.79763374097075,532.13830093176216],"cornerRadius":0,"prevPoint":[605.67266977751206,16.47175369678871],"inPoint":[308.30822019811342,503.80404406807986],"nextPoint":[605.67266977751206,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[605.67266977751206,16.47175369678871],"opacity":1,"blur":0,"isLocked":false,"gid":45,"smootheningRate":0,"initialPoint":[605.67266977751206,16.47175369678871],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[307.98765662154068,497.22766759942954],"reflectionModeOverride":0,"anchorPoint":[307.98765662154068,526.40806463936553],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[307.98765662154068,555.58846167930153],"nextPoint":[0,0]},{"outPoint":[347.07124677007465,468.5633046399771],"reflectionModeOverride":0,"anchorPoint":[331.56894097295356,478.9361833057668],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[316.06663517583246,489.3090619715565],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":33,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.7690644381571,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.7690644381571,667.02363476289884],"cornerRadius":0,"prevPoint":[72.193980608055426,108.16792868331186],"inPoint":[368.26333755475412,638.68961321030247],"nextPoint":[72.193980608055426,108.16792868331186]},{"outPoint":[367.82059343747073,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.82059343747073,616.38299074283532],"cornerRadius":0,"prevPoint":[55.02340366515682,81.837967291336099],"inPoint":[367.82059343747073,616.38299074283532],"nextPoint":[55.02340366515682,81.837967291336099]},{"outPoint":[383.13716259159514,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[403.6767241609266,616.33647011725679],"cornerRadius":0,"prevPoint":[70.109671854888575,134.1591558035534],"inPoint":[403.6767241609266,616.33647011725679],"nextPoint":[70.109671854888575,134.1591558035534]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[72.357622652088821,121.79167358010636],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[72.357622652088821,121.79167358010636],"creationPoints":[],"name":"(curve)"}]}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-2.4740749822735211,26.734403557988003],"opacity":1,"blur":0,"isLocked":false,"gid":46,"smootheningRate":0,"initialPoint":[-2.4740749822735211,26.734403557988003],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-17.790763092914176,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[-17.790763092914176,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-17.790763092914176,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-118.14742861878335,355.38893101990755],"reflectionModeOverride":0,"anchorPoint":[-118.14742861878335,355.38893101990755],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-118.14742861878335,355.38893101990755],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-89.082653539453304,326.03057263672883],"reflectionModeOverride":0,"anchorPoint":[-89.082653539453304,326.03057263672883],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-89.082653539453304,326.03057263672883],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,406.22781863362536],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,406.22781863362536],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,406.22781863362536],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,214.66453204993843],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,214.66453204993843],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,214.66453204993843],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,359.98840044701058],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,359.98840044701058],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,359.98840044701058],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[40.289832016581158,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[40.289832016581158,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[40.289832016581158,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,551.89420905136228],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,551.89420905136228],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,551.89420905136228],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,697.21808678120522],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,697.21808678120522],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,697.21808678120522],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,505.65480953028907],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,505.65480953028907],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,505.65480953028907],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-88.78907023560464,585.60738760822585],"reflectionModeOverride":0,"anchorPoint":[-88.78907023560464,585.60738760822585],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-88.78907023560464,585.60738760822585],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-117.85384531493469,556.24902922504714],"reflectionModeOverride":0,"anchorPoint":[-117.85384531493469,556.24902922504714],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-117.85384531493469,556.24902922504714],"nextPoint":[-343.22817622315347,103.6410041510419]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-343.22817622315347,103.6410041510419],"opacity":1,"blur":0,"isLocked":false,"gid":13,"smootheningRate":0,"initialPoint":[-343.22817622315347,103.6410041510419],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-87.50742636811205,292.97453151588081],"reflectionModeOverride":0,"anchorPoint":[-87.50742636811205,292.97453151588081],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.50742636811205,292.97453151588081],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-78.027426825875665,283.48453174476265],"reflectionModeOverride":0,"anchorPoint":[-78.027426825875665,283.48453174476265],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-78.027426825875665,283.48453174476265],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-87.507426368111936,274.01453147773384],"reflectionModeOverride":0,"anchorPoint":[-87.507426368111936,274.01453147773384],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.507426368111936,274.01453147773384],"nextPoint":[-164.14742861878338,191.97453151588081]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588081],"opacity":1,"blur":0,"isLocked":false,"gid":14,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588081],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":11,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588078],"creationPoints":[],"name":"(curve)"}],"cacheLayers":[],"cacheArtboardPaths":[],"undoStack":[[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11,6]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -29.209948435277369, -232.41660739687057]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14,15]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.01450315572447696,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93007444840156717,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93451308072143913,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93910701763028559,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94055809677531532,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94999337125602745,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95297621548706413,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95790068402842177,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.011044777045815676,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96266052064980523,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0088583736096398309,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96911575815444173,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0077646546444650423,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97217928832645584,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0032726223185911016,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98016728703982747,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0010851843882415254,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98662352066719217,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99307006473711057,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.20437110010339629, -0, -0, 0.20437110010339629, 126.38769661378409, 170.79330548171168]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[13]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":2,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":186.05023255601679,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":558.85414169234264,\"OutPoint_X\":186.05023255601679,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":558.85414169234264},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":1,\"NodePoints\":{\"inPoint_X\":235.46249740746129,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":509.44413050490067,\"OutPoint_X\":136.6379677045723,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":608.26415287978466},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.03408561318607653,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.029926881951800856,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.021585238181938561,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.012961911164688478,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.011895648503707629,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.007872629590133573,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.005957393322960804,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-4.3711390001862419e-08, -0.99999999999999922, 0.99999999999999922, -4.3711390001862419e-08, -173.29768953296772, 744.95852622656867]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 8.2190355245806472, 176.90598115888827]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.55538688884063114, -0, 182.49084631957811]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 150.32620239929324, 36.081797695525552]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12249755187863798, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.64978701032960984, -0, -0, 0.64978701032960984, 52.531948450558538, 142.05406077476505]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.171276915352394, 196.69205141995428]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.65616231221472443, -0, -0, 0.65616231221472443, 51.425334356867019, 141.12771095949628]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.53221494113933, 129.60740108742715]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.17127691535239, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.09503537570964, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 208.92985571850454]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 130.84123782858848]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 75]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 4.5418914390142788, -306.56258907512347]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.96223302635797447, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0392493219135692, -0, -0, 1.0392493219135692, 0, -2.9436991435176969]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 15.458108560985721, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 26.562589075123469]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 50]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":22}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 9.881426012744555, 0.91199734232554874]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -77.745954011856824, 28.270796276850916]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 67.864527999112269, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.1538461432654479, -0, -0, 1, -1.5384614326544779, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -136.98574299882625, 7.8612196478959504]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -53.141264363887032, -43.604121250556744]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -70.052055171480333, -2.5049969673447094]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 219.77475934149726, 163.57319520938375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.6846814845770105, -0, -0, 0.6846814845770105, -0.080018108436871196, 3.1890182924268196]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -85.587957051898314, -21.453286888159028]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":142.86303913891427,\"AnchorPoint_Y\":297.21030751597522,\"inPoint_Y\":297.21030751597522,\"OutPoint_X\":142.86303913891427,\"AnchorPoint_X\":142.86303913891427,\"OutPoint_Y\":297.21030751597522},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -258.8309537310077, 14.790477788784642]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.98660023945785613, -0, -0, 1.0127745463822073, 7.5897658853896965, -3.7483052243093384]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":307.98765662154068,\"AnchorPoint_Y\":526.40806463936553,\"inPoint_Y\":555.58846167930153,\"OutPoint_X\":307.98765662154068,\"AnchorPoint_X\":307.98765662154068,\"OutPoint_Y\":497.22766759942954},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":9223372036854775807}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":580,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":680.00002878904479},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":660,\"inPoint_Y\":620,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":700},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 20, 20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 640, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 39, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":220,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 60, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{540, 0}\",\"VNPrevPointForRadiusKey\":\"{540, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":300,\"AnchorPoint_X\":280,\"OutPoint_Y\":600},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[],"methodSignature":"reversePathDirection","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":600,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":240,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -60, 100]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -8.3360061810856791, -0.18596575427056905]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12624915594960839, -0.84513498021431133]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0820554726071352, -0, -0, 1, -28.572516179113212, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 3.7693360211719664, 0.58007166688469169]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.8911271920692454, -0, 58.840421083655528]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0.018101184397323777, -6.1834757589472247]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.5616683932951787, -14.721591768371582]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.4335340527209723, 13.544422687729025]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":43}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.10440918421369,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":598.11903819000008},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":366.0442444460744,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.21462016330969},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":337.32713106884489,\"AnchorPoint_Y\":505.14299981511687,\"inPoint_Y\":505.14299981511687,\"OutPoint_X\":337.32713106884489,\"AnchorPoint_X\":337.32713106884489,\"OutPoint_Y\":505.14299981511687},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":282.38947067174871,\"AnchorPoint_Y\":502.68576551434728,\"inPoint_Y\":502.68576551434728,\"OutPoint_X\":282.38947067174871,\"AnchorPoint_X\":282.38947067174871,\"OutPoint_Y\":502.68576551434728},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"VNPrevPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":303.99512941310888,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":487.18557523329599,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":45}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 618.5121544161118, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -17.095091220096265, 18.636572344325828]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 2.4740749822735211, -26.734403557988003]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[46]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":50}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":50}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":323.12940602919167,\"AnchorPoint_Y\":534.54856008655031,\"inPoint_Y\":534.54856008655031,\"OutPoint_X\":323.12940602919167,\"AnchorPoint_X\":323.12940602919167,\"OutPoint_Y\":534.54856008655031},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[47,48]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":46},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13822251558303833,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -2.1531569701203352, 25.991399082341331]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":308.00218177519929,\"AnchorPoint_Y\":532.86800447564667,\"inPoint_Y\":504.53379546064855,\"OutPoint_X\":307.49158198784681,\"AnchorPoint_X\":307.49158198784681,\"OutPoint_Y\":532.86800447564667},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.54481307650724,\"AnchorPoint_Y\":482.22702540901258,\"inPoint_Y\":482.22702540901258,\"OutPoint_X\":307.54481307650724,\"AnchorPoint_X\":307.54481307650724,\"OutPoint_Y\":482.22702540901258},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"VNPrevPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":344.58533198784681,\"AnchorPoint_Y\":482.18050447564667,\"inPoint_Y\":482.18050447564667,\"OutPoint_X\":323.06817538141632,\"AnchorPoint_X\":344.58533198784681,\"OutPoint_Y\":488.07641427863712},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.34003453224391933]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.1954377384541317]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 692.07691397569363, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 57.024957634971202, 19.792602829855639]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":307.04977456552314,\"AnchorPoint_Y\":532.53992937509292,\"inPoint_Y\":504.20572036009486,\"OutPoint_X\":307.56037435287561,\"AnchorPoint_X\":307.56037435287561,\"OutPoint_Y\":532.53992937509292},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"VNPrevPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.50714326421519,\"AnchorPoint_Y\":481.89895030845889,\"inPoint_Y\":481.89895030845889,\"OutPoint_X\":307.50714326421519,\"AnchorPoint_X\":307.50714326421519,\"OutPoint_Y\":481.89895030845889},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"VNPrevPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":270.46662435287561,\"AnchorPoint_Y\":481.85242937509298,\"inPoint_Y\":481.85242937509298,\"OutPoint_X\":291.68464211968808,\"AnchorPoint_X\":270.46662435287561,\"OutPoint_Y\":491.96348695323337},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"VNPrevPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":55},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0330248439984473, -0, -0, 0.99999999999999989, -11.379876833051835, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[55]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":56},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":55}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":57}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -290.115478515625, 62.302734375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.89467432944640213,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.90097236067321962,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.031593452065677957,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.91739226661025597,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.019517801575741518,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93616537665754818,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0091232041181144082,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94328004740819738,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0018620895127118644,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95006183205443251,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95531556443573817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9587083584477003,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96669617604784863,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96977202191904677,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97283138648692502,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97731792325435829,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9818318081185089,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9863455118694362,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99094089768406901,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99553628349870182,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -138.240478515625, -358.288818359375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10.2197265625, 7.887451171875]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.73681640625, -1.3956298828125]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96954748896640086,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96362693977790437,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.95841712343394081,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.954131593465262,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.93934857275056949,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.83357840083997725,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.80253639308086555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.76741239856207288,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.7565612097807517,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.73508462058656032,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.69529915290432798,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.68285743522209563,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.66214385499715267,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.64970324957289294,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.63305608449601369,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.62061547907175396,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.60169597095671978,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.59555519468963558,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5870419721668565,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.57588046341116172,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.56733832218109337,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5457271497722096,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.51220146996013671,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.45939702270785876,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.41278562784738043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.38011749893223234,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.35006206399487472,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.33336373505125283,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31040673049544421,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29577720138097952,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.27499243664578588,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.25639993237471526,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24782776017938496,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23669294561503418,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22722206897779043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22344372864464693,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21991675861332574,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21874110193621868,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2197966347522779,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23113499252562641,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24180488325740318,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2529452591116173,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.28146132901480636,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29042946504840544,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29303437322038722,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30319262528473806,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31050016016514809,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31284924900341687,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31402379342255127,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31192273811218679,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31087054207004555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30975717183940776,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.3074892778331435,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0000066161593433, -0, -0, 1.0000066161593433, -0.00084115369224984499, -0.0015278888873493538]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 106.25150911801597, 215.10157288799655]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1024, 1024}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1018.1834573966, 1022.316502396953}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1008.5644489681886, 1015.9218488910001}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1003.3383988868811, 1012.188740268123}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {959.56182404754463, 968.37466824766148}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {930.99873639690077, 938.04865943448704}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {883.88375480634159, 898.86742705675647}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {851.39277451811745, 876.13749149913815}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {807.42735481491377, 847.80421878677203}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {780.8131139470363, 833.66058405713125}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {758.18676120653936, 823.6793880913026}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {745.42543866806363, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.45307691385142, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.65313908147255}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {735.7456294412641, 820.1359296408807}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {734.20024226104533, 819.105537302637}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {730.82720724054207, 815.73250228213374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {722.51078545309133, 807.41391622785272}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {715.22999050970566, 800.13291995732015}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {710.02528110597814, 794.3195482564854}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {708.99086222479468, 792.76802059828378}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {705.03881032957679, 786.83843280185465}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {703.71729893680003, 785.04591654871797}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {702.86608775936838, 784.19455437592615}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {701.16326275021129, 782.49188036212945}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {700.77188277648224, 782.10024872946656}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.97704320020512, 781.30550981676288}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.12865060283116, 780.87985389626033}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {698.70304501411556, 780.45414764397106}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {695.54100084364723, 776.8204946317021}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {693.70127337454369, 774.98056583545167}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {691.86235121402819, 772.67134345959084}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.97672115541332, 769.84279770809485}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.03491276184081, 768.90119064166925}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {686.67817717929256, 766.07264489017348}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {683.90469440249581, 763.76603976722345}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {682.53607245732564, 762.86897633206718}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {681.17026909221318, 762.39756881741346}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {678.95083862390538, 761.54620664462163}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {676.30580256688177, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.07408308165805, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.28025820206608, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {671.48603066818009, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.54341696601955, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.17600298373122, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.39103649860635, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.44842279644581, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.02281720772999, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.226367014277, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {663.57830298909857, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.7291050831368, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.92701772956843, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.54691207607016, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.69429082217562}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.30406847954168}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.91817467056762}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.52795232793369}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.12605301077474}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.73814593033103}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.96107347477482}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.97694686201953, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 757.18274272455005}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 756.35493582795493}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.94518475206382}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.5517915068649}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.78614436689986}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.3960730196261}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.00237778370661}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.60918586565458}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.21065877820774}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.81701387407497}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.41430924832821}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 749.69714774089903}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 748.93285955917622}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.99004452986878}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.13868235707696}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.41906127678772, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.65522608114543, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.27149653900142, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.88132452815421, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.48793128295529, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.69429966956, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.30453031300658, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.68503862079888, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.0695734715307, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.47263041978476, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.86843959074758, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.25498771294906, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.63670398362319, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.02566803158879, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.83158060094979, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {664.08383545516563, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.7862578100287, 746.7378903392264}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.06307457617595, 747.16359659151567}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.70851328749336, 748.01490843252077}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.98533005364084, 748.44061468481004}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.66399580516031, 748.84437627807847}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.94081257130756, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.19387273411121, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.58525270784025, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.20474440004818, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.93235683113426, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.1381292972485, 747.52296554887494}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.87500277709569, 747.09725929658589}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.47194582884163, 746.2471050866759}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.64368594616599, 745.41864387685314}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 745.02308636482417}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":330.67709661876142,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":247.83495910094825},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":214.25650324349817,\"inPoint_Y\":214.25650324349817,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":214.25650324349817},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":247.83495910094825,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":330.67709661876142},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":364.25550551101242,\"inPoint_Y\":364.25550551101242,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":364.25550551101242},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":580.67544258986891,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":497.83330507205574},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":464.25484921460566,\"inPoint_Y\":464.25484921460566,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":464.25484921460566},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":497.83330507205574,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":580.67544258986891},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":614.2538514821199,\"inPoint_Y\":614.2538514821199,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":614.2538514821199},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":97.57351080287799,\"AnchorPoint_Y\":309.16721333385931,\"inPoint_Y\":309.16721333385931,\"OutPoint_X\":97.57351080287799,\"AnchorPoint_X\":97.57351080287799,\"OutPoint_Y\":309.16721333385931},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":221.36797043606543,\"AnchorPoint_Y\":443.90288627635027,\"inPoint_Y\":443.90288627635027,\"OutPoint_X\":221.36797043606543,\"AnchorPoint_X\":221.36797043606543,\"OutPoint_Y\":443.90288627635027},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":361.08361414401247,\"AnchorPoint_Y\":309.6035637643763,\"inPoint_Y\":309.6035637643763,\"OutPoint_X\":361.08361414401247,\"AnchorPoint_X\":361.08361414401247,\"OutPoint_Y\":309.6035637643763},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":377.82365759764593,\"AnchorPoint_Y\":307.07623767819223,\"inPoint_Y\":307.07623767819223,\"OutPoint_X\":377.82365759764593,\"AnchorPoint_X\":377.82365759764593,\"OutPoint_Y\":307.07623767819223},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":503.2994629767461,\"AnchorPoint_Y\":440.11243356580462,\"inPoint_Y\":440.11243356580462,\"OutPoint_X\":503.2994629767461,\"AnchorPoint_X\":503.2994629767461,\"OutPoint_Y\":440.11243356580462},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":644.91269001662772,\"AnchorPoint_Y\":307.50708423933065,\"inPoint_Y\":307.50708423933065,\"OutPoint_X\":644.91269001662772,\"AnchorPoint_X\":644.91269001662772,\"OutPoint_Y\":307.50708423933065},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":516.45695350382334,\"AnchorPoint_Y\":154.80813254227905,\"inPoint_Y\":154.80813254227905,\"OutPoint_X\":516.45695350382334,\"AnchorPoint_X\":516.45695350382334,\"OutPoint_Y\":154.80813254227905},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":223.97172601151692,\"AnchorPoint_Y\":448.88181029919406,\"inPoint_Y\":448.88181029919406,\"OutPoint_X\":223.97172601151692,\"AnchorPoint_X\":223.97172601151692,\"OutPoint_Y\":448.88181029919406},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":78.252109034647219,\"AnchorPoint_Y\":300.1041732651538,\"inPoint_Y\":300.1041732651538,\"OutPoint_X\":78.252109034647219,\"AnchorPoint_X\":78.252109034647219,\"OutPoint_Y\":300.1041732651538},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":648.07861404640448,\"AnchorPoint_Y\":302.9355554780409,\"inPoint_Y\":302.9355554780409,\"OutPoint_X\":648.07861404640448,\"AnchorPoint_X\":648.07861404640448,\"OutPoint_Y\":302.9355554780409},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":506.17105679039673,\"AnchorPoint_Y\":444.7847840681311,\"inPoint_Y\":444.7847840681311,\"OutPoint_X\":506.17105679039673,\"AnchorPoint_X\":506.17105679039673,\"OutPoint_Y\":444.7847840681311},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":217.51314851045686,\"AnchorPoint_Y\":156.11368443580113,\"inPoint_Y\":156.11368443580113,\"OutPoint_X\":217.51314851045686,\"AnchorPoint_X\":217.51314851045686,\"OutPoint_Y\":156.11368443580113},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":362.67570746371234,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.18143434711533,\"AnchorPoint_X\":362.18143434711533,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.23296334642896,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.23296334642896,\"AnchorPoint_X\":362.23296334642896,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":398.08909406988482,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":377.54953250055337,\"AnchorPoint_X\":398.08909406988482,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":361.7537539269963,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.24802704359331,\"AnchorPoint_X\":362.24802704359331,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.19649804427968,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.19649804427968,\"AnchorPoint_X\":362.19649804427968,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":326.34036732082382,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":346.87992889015482,\"AnchorPoint_X\":326.34036732082382,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":\"Mac App icon\"}","argumentGID":0}],"methodSignature":"title","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":57},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 51.701018329683393, 116.18082292683278]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {740, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {736.35253901673036, 740.35218585345365}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {726.62032261171248, 732.07890244371663}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {706.29497285008392, 715.91557216276851}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {691.48129050959142, 702.64569380957585}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {676.51521301916046, 688.7168285032127}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.9666932377902, 683.82682021816697}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.51490451806353, 675.17444423582754}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.05376131978483, 661.73639796317264}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {652.798107458392, 653.69283161018325}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {646.24074717873964, 645.29847372318682}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {639.28068335784974, 635.32950515648088}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.62597526532704, 630.02008897143548}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.24953610331818, 624.67241338007113}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 622.6021329778589}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 621.53096597223009}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 620.05992231684661}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 617.99269367248519}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 616.51850188795038}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 616.24683119169595}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 615.70516023506332}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 615.17627453763725}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 614.91966988803563}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 614.41860337270202}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 614.17012603611374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.94885764559763, 613.92274090759827}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.49213479057289, 613.12141354351331}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.76223142225149, 612.8513490356014}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.57611917917507, 612.03746127867794}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.11913933401547, 611.49444112383765}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 610.95251317707016}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 609.88031821090215}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 609.63566360256891}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 609.14548704419769}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.39077790650299, 605.7312446082625}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 599.34404392161696}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 593.09060660013677}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 589.48773340525565}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 588.67320317299516}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 588.41560268662124}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 588.17014498411686}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 587.37090566487711}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.5843428634887, 585.89687449917653}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.75570389792438, 583.82575887902624}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.35654683309554, 582.08108497761054}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 581.53800057523642}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 581.26632987898211}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 580.49002692927365}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 579.01760195191559}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 577.54289618711107}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 576.39787664149299}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {638.89982397807057, 574.98324232069888}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {637.49819978285086, 574.14796013505304}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {636.68559697660157, 573.33526095750301}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {635.54147689645515, 572.22030925765694}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {634.72784612966643, 571.9486064376357}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {633.91472934314743, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {632.17002331796471, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {630.76017543843136, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {630.21689829345632, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {629.95939417838304, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {629.16606563224377, 571.67857405349071}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.41930368598264, 572.55198715038807}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {626.22070169725794, 573.15125602098351}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {624.65229090455068, 574.07519980313668}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.58506226018949, 575.54563310695005}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.31316669756711, 575.81730380320448}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.51598329940589, 576.35817166566585}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.75935295706495, 576.11476988423976}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.01171726944222, 576.11476988423976}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.79887805234603, 575.59915130252296}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {623.61379376980881, 574.78423558506029}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.35849979499153, 574.21346049566114}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {626.4990220132504, 573.64088647531833}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 573.36995462570155}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 573.12317984875619}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 572.87576259647392}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 572.62320554149551}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 572.10858279655099}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {627.02790771067657, 571.85628273170744}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {626.48437357556668, 571.58695707043307}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.96371156245436, 571.06635930485447}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.71160424021173, 571.06635930485447}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.46360876012614, 571.06635930485447}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {625.21124444774887, 570.81418773507824}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {624.70805776380325, 570.81418773507824}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {624.18174196772497, 570.28806468160133}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {623.92449484278654, 570.01636186157998}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {623.11163504640217, 569.74465904155875}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.57632459560614, 569.20931646699569}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.31933446080234, 569.20931646699569}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.04743889817996, 568.93899496894903}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {622.04743889817996, 568.68393226015633}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.80252729971198, 568.68393226015633}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {621.80252729971198, 570.51884182265508}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {600, 570.51884182265508}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 15.687962779094846, 1.6061883343354566e-05]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}]],"isGroupingUndo":false,"currentUndoGroup":[],"currentRedoGroup":[],"isGroupingRedo":false,"redoStack":[],"nextGID":57} diff --git a/docs/images/logo_framed.vectornator/Artboard0.json b/docs/images/logo_framed.vectornator/Artboard0.json index 7a365f7..a4e4bb4 100644 --- a/docs/images/logo_framed.vectornator/Artboard0.json +++ b/docs/images/logo_framed.vectornator/Artboard0.json @@ -1 +1 @@ -{"layers":[{"elements":[{"elementDescription":"(polygon)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[368.75294547403814,729.44134214300266],"reflectionModeOverride":0,"anchorPoint":[368.75294547403814,729.44134214300266],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75294547403814,729.44134214300266],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884741476317231,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[58.884741476317231,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884741476317231,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884771993693448,192.73377248033557],"reflectionModeOverride":0,"anchorPoint":[58.884771993693448,192.73377248033557],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884771993693448,192.73377248033557],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[368.75296538097359,13.831330639116459],"reflectionModeOverride":0,"anchorPoint":[368.75296538097359,13.831330639116459],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75296538097359,13.831330639116459],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62124178681802,192.73392506721666],"reflectionModeOverride":0,"anchorPoint":[678.62124178681802,192.73392506721666],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62124178681802,192.73392506721666],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62118075206558,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[678.62118075206558,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62118075206558,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":0.3074892778331435},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":18.646402359008789,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[368.75296111419152,729.44134214300266],"opacity":1,"blur":0,"isLocked":false,"gid":57,"smootheningRate":0,"initialPoint":[368.75296111419152,371.63633639105956],"creationPoints":[],"name":"(polygon)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[61.506041898417834,134.69462453150436],"opacity":1,"blur":0,"isLocked":false,"gid":56,"smootheningRate":0,"initialPoint":[61.506041898417834,134.69462453150436],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[292.87340185278117,214.25650324349817],"opacity":1,"blur":0,"isLocked":false,"gid":27,"smootheningRate":0,"initialPoint":[292.87340185278117,214.25650324349817],"creationPoints":[],"group":{"elements":[{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.87340185278117,289.25600703483042],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,289.25600703483042],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[292.87340185278117,289.25600703483042],"nextPoint":[64.939207660974262,-881.2368652940595]},{"outPoint":[442.87240655028904,289.25600703483042],"reflectionModeOverride":0,"anchorPoint":[442.87240655028904,289.25600703483042],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[442.87240655028904,289.25600703483042],"nextPoint":[64.939207660974262,-881.2368652940595]},{"outPoint":[442.87240655028904,539.25435579077384],"reflectionModeOverride":0,"anchorPoint":[442.87240655028904,539.25435579077384],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[442.87240655028904,539.25435579077384],"nextPoint":[64.939207660974262,-881.2368652940595]},{"outPoint":[292.87340185278117,539.25435579077384],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,539.25435579077384],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[292.87340185278117,539.25435579077384],"nextPoint":[64.939207660974262,-881.2368652940595]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[442.87240655028904,539.25435579077384],"opacity":1,"blur":0,"isLocked":false,"gid":22,"smootheningRate":0,"initialPoint":[292.87340185278117,289.25600703483042],"creationPoints":[],"name":"(rectangle)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.87340185278117,247.83495910094825],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,289.25602785985438],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[292.87340185278117,330.67709661876142],"nextPoint":[-167.96486361526627,-465.50590420624349]},{"outPoint":[409.29399522804465,214.25650324349817],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,214.25650324349817],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[326.45185771023148,214.25650324349817],"nextPoint":[-167.96486361526627,-465.50590420624349]},{"outPoint":[442.87240412029564,330.67709661876142],"reflectionModeOverride":0,"anchorPoint":[442.87240412029564,289.25602785985438],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[442.87240412029564,247.83495910094825],"nextPoint":[-167.96486361526627,-465.50590420624349]},{"outPoint":[326.45185771023148,364.25550551101242],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,364.25550551101242],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[409.29399522804465,364.25550551101242],"nextPoint":[-167.96486361526627,-465.50590420624349]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-167.96486361526627,-465.50590420624349],"opacity":1,"blur":0,"isLocked":false,"gid":24,"smootheningRate":0,"initialPoint":[-167.96486361526627,-465.50590420624349],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.87340185278117,497.83330507205574],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,539.25437383096278],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[292.87340185278117,580.67544258986891],"nextPoint":[-167.96486361526627,-215.50755823513555]},{"outPoint":[409.29399522804465,464.25484921460566],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,464.25484921460566],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[326.45185771023148,464.25484921460566],"nextPoint":[-167.96486361526627,-215.50755823513555]},{"outPoint":[442.87240412029564,580.67544258986891],"reflectionModeOverride":0,"anchorPoint":[442.87240412029564,539.25437383096278],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[442.87240412029564,497.83330507205574],"nextPoint":[-167.96486361526627,-215.50755823513555]},{"outPoint":[326.45185771023148,614.2538514821199],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,614.2538514821199],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[409.29399522804465,614.2538514821199],"nextPoint":[-167.96486361526627,-215.50755823513555]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-167.96486361526627,-215.50755823513555],"opacity":1,"blur":0,"isLocked":false,"gid":25,"smootheningRate":0,"initialPoint":[-167.96486361526627,-215.50755823513555],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[302.87333569162547,289.07321462503751],"reflectionModeOverride":0,"anchorPoint":[302.87333569162547,289.07321462503751],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[302.87333569162547,289.07321462503751],"nextPoint":[105.33036558060121,-881.41965770385173]},{"outPoint":[432.87247428821752,289.07321462503751],"reflectionModeOverride":0,"anchorPoint":[432.87247428821752,289.07321462503751],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[432.87247428821752,289.07321462503751],"nextPoint":[105.33036558060121,-881.41965770385173]},{"outPoint":[432.87247428821752,539.07156338098093],"reflectionModeOverride":0,"anchorPoint":[432.87247428821752,539.07156338098093],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[432.87247428821752,539.07156338098093],"nextPoint":[105.33036558060121,-881.41965770385173]},{"outPoint":[302.87333569162547,539.07156338098093],"reflectionModeOverride":0,"anchorPoint":[302.87333569162547,539.07156338098093],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[302.87333569162547,539.07156338098093],"nextPoint":[105.33036558060121,-881.41965770385173]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[432.87247428821752,539.07156338098093],"opacity":1,"blur":0,"isLocked":false,"gid":26,"smootheningRate":0,"initialPoint":[302.87333569162547,289.07321462503751],"creationPoints":[],"name":"(rectangle)"}]},"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[103.16114089391976,309.16721333385931],"reflectionModeOverride":0,"anchorPoint":[103.16114089391976,309.16721333385931],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[103.16114089391976,309.16721333385931],"nextPoint":[84.093506586570356,146.69454513811797]},{"outPoint":[226.95560052710721,443.90288627635027],"reflectionModeOverride":0,"anchorPoint":[226.95560052710721,443.90288627635027],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[226.95560052710721,443.90288627635027],"nextPoint":[84.093506586570356,146.69454513811797]},{"outPoint":[366.67124423505425,309.6035637643763],"reflectionModeOverride":0,"anchorPoint":[366.67124423505425,309.6035637643763],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[366.67124423505425,309.6035637643763],"nextPoint":[84.093506586570356,146.69454513811797]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[84.093506586570356,146.69454513811797],"opacity":1,"blur":0,"isLocked":false,"gid":29,"smootheningRate":0,"initialPoint":[84.093506586570356,146.69454513811797],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[383.41128768868771,307.07623767819223],"reflectionModeOverride":0,"anchorPoint":[383.41128768868771,307.07623767819223],"cornerRadius":0,"prevPoint":[364.08468148613224,146.65290478922907],"inPoint":[383.41128768868771,307.07623767819223],"nextPoint":[364.08468148613224,146.65290478922907]},{"outPoint":[508.88709306778787,440.11243356580462],"reflectionModeOverride":0,"anchorPoint":[508.88709306778787,440.11243356580462],"cornerRadius":0,"prevPoint":[364.08468148613224,146.65290478922907],"inPoint":[508.88709306778787,440.11243356580462],"nextPoint":[364.08468148613224,146.65290478922907]},{"outPoint":[650.50032010766949,307.50708423933065],"reflectionModeOverride":0,"anchorPoint":[650.50032010766949,307.50708423933065],"cornerRadius":0,"prevPoint":[364.08468148613224,146.65290478922907],"inPoint":[650.50032010766949,307.50708423933065],"nextPoint":[364.08468148613224,146.65290478922907]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[364.08468148613224,146.65290478922907],"opacity":1,"blur":0,"isLocked":false,"gid":30,"smootheningRate":0,"initialPoint":[364.08468148613224,146.65290478922907],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[522.04458359486512,154.80813254227905],"reflectionModeOverride":0,"anchorPoint":[522.04458359486512,154.80813254227905],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[522.04458359486512,154.80813254227905],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[229.5593561025587,448.88181029919406],"reflectionModeOverride":0,"anchorPoint":[229.5593561025587,448.88181029919406],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[229.5593561025587,448.88181029919406],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[83.839739125688993,300.1041732651538],"reflectionModeOverride":0,"anchorPoint":[83.839739125688993,300.1041732651538],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[83.839739125688993,300.1041732651538],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[653.66624413744626,302.9355554780409],"reflectionModeOverride":0,"anchorPoint":[653.66624413744626,302.9355554780409],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[653.66624413744626,302.9355554780409],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[511.75868688143851,444.7847840681311],"reflectionModeOverride":0,"anchorPoint":[511.75868688143851,444.7847840681311],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[511.75868688143851,444.7847840681311],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[223.10077860149863,156.11368443580113],"reflectionModeOverride":0,"anchorPoint":[223.10077860149863,156.11368443580113],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[223.10077860149863,156.11368443580113],"nextPoint":[1039.3203857761648,-115.61701281386331]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":90,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[1200.0148931967549,-113.13323396127316],"opacity":1,"blur":0,"isLocked":false,"gid":21,"smootheningRate":0,"initialPoint":[1200.0148931967549,-113.13323396127316],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[295.85728153799414,418.73751961026619],"reflectionModeOverride":0,"anchorPoint":[295.85728153799414,418.73751961026619],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[295.85728153799414,418.73751961026619],"nextPoint":[84.093506586570356,146.69454513811797]},{"outPoint":[438.89979217720088,418.73751961026619],"reflectionModeOverride":0,"anchorPoint":[438.89979217720088,418.73751961026619],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[438.89979217720088,418.73751961026619],"nextPoint":[84.093506586570356,146.69454513811797]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[84.093506586570356,146.69454513811797],"opacity":1,"blur":0,"isLocked":false,"gid":31,"smootheningRate":0,"initialPoint":[84.093506586570356,146.69454513811797],"creationPoints":[],"name":"(line)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[295.85728153799414,521.73683815036247],"reflectionModeOverride":0,"anchorPoint":[295.85728153799414,521.73683815036247],"cornerRadius":0,"prevPoint":[84.093506586570356,249.69386367821471],"inPoint":[295.85728153799414,521.73683815036247],"nextPoint":[84.093506586570356,249.69386367821471]},{"outPoint":[438.89979217720088,521.73683815036247],"reflectionModeOverride":0,"anchorPoint":[438.89979217720088,521.73683815036247],"cornerRadius":0,"prevPoint":[84.093506586570356,249.69386367821471],"inPoint":[438.89979217720088,521.73683815036247],"nextPoint":[84.093506586570356,249.69386367821471]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[84.093506586570356,249.69386367821471],"opacity":1,"blur":0,"isLocked":false,"gid":32,"smootheningRate":0,"initialPoint":[84.093506586570356,249.69386367821471],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[70.109671854888575,134.69462453150436],"opacity":1,"blur":0,"isLocked":false,"gid":55,"smootheningRate":0,"initialPoint":[70.109671854888575,134.69462453150436],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.7690644381571,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.7690644381571,667.02363476289884],"cornerRadius":0,"prevPoint":[72.193980608055426,108.16792868331186],"inPoint":[368.26333755475412,638.68961321030247],"nextPoint":[72.193980608055426,108.16792868331186]},{"outPoint":[367.82059343747073,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.82059343747073,616.38299074283532],"cornerRadius":0,"prevPoint":[55.02340366515682,81.837967291336099],"inPoint":[367.82059343747073,616.38299074283532],"nextPoint":[55.02340366515682,81.837967291336099]},{"outPoint":[383.13716259159514,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[403.6767241609266,616.33647011725679],"cornerRadius":0,"prevPoint":[70.109671854888575,134.1591558035534],"inPoint":[403.6767241609266,616.33647011725679],"nextPoint":[70.109671854888575,134.1591558035534]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[72.357622652088821,121.79167358010636],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[72.357622652088821,121.79167358010636],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.83565713463508,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.83565713463508,667.02363476289884],"cornerRadius":0,"prevPoint":[663.41074096473653,108.16792868331186],"inPoint":[367.34138401803807,638.68961321030247],"nextPoint":[663.41074096473653,108.16792868331186]},{"outPoint":[367.78412813532145,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.78412813532145,616.38299074283532],"cornerRadius":0,"prevPoint":[680.58131790763514,81.837967291336099],"inPoint":[367.78412813532145,616.38299074283532],"nextPoint":[680.58131790763514,81.837967291336099]},{"outPoint":[352.46755898119659,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[331.92799741186559,616.33647011725679],"cornerRadius":0,"prevPoint":[665.49504971790361,134.1591558035534],"inPoint":[331.92799741186559,616.33647011725679],"nextPoint":[665.49504971790361,134.1591558035534]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[663.24709892070291,121.99906937808692],"opacity":1,"blur":0,"isLocked":false,"gid":54,"smootheningRate":0,"initialPoint":[663.24709892070291,121.99906937808692],"creationPoints":[],"name":"(curve)"}]},"name":"(curve)"}]},"name":"(curve)"}],"isExpanded":false,"isLocked":false,"isVisible":true,"opacity":1,"gid":4,"name":"Layer 1"}],"frame":{"y":0,"x":0,"width":745,"height":744.6337699943515},"title":"Mac App icon","activeLayerIndex":0,"settings":{"gridSpacing":20,"gridAngle":45,"backgroundColor":{"b":1,"s":0,"h":0,"a":1},"gridMode":0,"isGridVisible":false},"guideLayer":{"isExpanded":false,"elements":[],"isLocked":false,"defaultName":"Guides","isVisible":true,"opacity":1,"name":"Guides","gid":5},"gid":3}
\ No newline at end of file +{"layers":[{"elements":[{"elementDescription":"(polygon)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[368.75294547403814,729.44134214300266],"reflectionModeOverride":0,"anchorPoint":[368.75294547403814,729.44134214300266],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75294547403814,729.44134214300266],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884741476317231,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[58.884741476317231,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884741476317231,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[58.884771993693448,192.73377248033557],"reflectionModeOverride":0,"anchorPoint":[58.884771993693448,192.73377248033557],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[58.884771993693448,192.73377248033557],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[368.75296538097359,13.831330639116459],"reflectionModeOverride":0,"anchorPoint":[368.75296538097359,13.831330639116459],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[368.75296538097359,13.831330639116459],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62124178681802,192.73392506721666],"reflectionModeOverride":0,"anchorPoint":[678.62124178681802,192.73392506721666],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62124178681802,192.73392506721666],"nextPoint":[221.86289110820167,-279.40236718212691]},{"outPoint":[678.62118075206558,550.53885452571922],"reflectionModeOverride":0,"anchorPoint":[678.62118075206558,550.53885452571922],"cornerRadius":0,"prevPoint":[221.86289110820167,-279.40236718212691],"inPoint":[678.62118075206558,550.53885452571922],"nextPoint":[221.86289110820167,-279.40236718212691]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":0.3074892778331435},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":18.646402359008789,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[368.75296111419152,729.44134214300266],"opacity":1,"blur":0,"isLocked":false,"gid":57,"smootheningRate":0,"initialPoint":[368.75296111419152,371.63633639105956],"creationPoints":[],"name":"(polygon)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[61.506041898417834,134.69462453150436],"opacity":1,"blur":0,"isLocked":false,"gid":56,"smootheningRate":0,"initialPoint":[61.506041898417834,134.69462453150436],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[292.87340185278117,214.25650324349817],"opacity":1,"blur":0,"isLocked":false,"gid":27,"smootheningRate":0,"initialPoint":[292.87340185278117,214.25650324349817],"creationPoints":[],"group":{"elements":[{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.87340185278117,289.25600703483042],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,289.25600703483042],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[292.87340185278117,289.25600703483042],"nextPoint":[64.939207660974262,-881.2368652940595]},{"outPoint":[442.87240655028904,289.25600703483042],"reflectionModeOverride":0,"anchorPoint":[442.87240655028904,289.25600703483042],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[442.87240655028904,289.25600703483042],"nextPoint":[64.939207660974262,-881.2368652940595]},{"outPoint":[442.87240655028904,539.25435579077384],"reflectionModeOverride":0,"anchorPoint":[442.87240655028904,539.25435579077384],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[442.87240655028904,539.25435579077384],"nextPoint":[64.939207660974262,-881.2368652940595]},{"outPoint":[292.87340185278117,539.25435579077384],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,539.25435579077384],"cornerRadius":0,"prevPoint":[64.939207660974262,-881.2368652940595],"inPoint":[292.87340185278117,539.25435579077384],"nextPoint":[64.939207660974262,-881.2368652940595]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[442.87240655028904,539.25435579077384],"opacity":1,"blur":0,"isLocked":false,"gid":22,"smootheningRate":0,"initialPoint":[292.87340185278117,289.25600703483042],"creationPoints":[],"name":"(rectangle)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.87340185278117,247.83495910094825],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,289.25602785985438],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[292.87340185278117,330.67709661876142],"nextPoint":[-167.96486361526627,-465.50590420624349]},{"outPoint":[409.29399522804465,214.25650324349817],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,214.25650324349817],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[326.45185771023148,214.25650324349817],"nextPoint":[-167.96486361526627,-465.50590420624349]},{"outPoint":[442.87240412029564,330.67709661876142],"reflectionModeOverride":0,"anchorPoint":[442.87240412029564,289.25602785985438],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[442.87240412029564,247.83495910094825],"nextPoint":[-167.96486361526627,-465.50590420624349]},{"outPoint":[326.45185771023148,364.25550551101242],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,364.25550551101242],"cornerRadius":0,"prevPoint":[-167.96486361526627,-465.50590420624349],"inPoint":[409.29399522804465,364.25550551101242],"nextPoint":[-167.96486361526627,-465.50590420624349]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-167.96486361526627,-465.50590420624349],"opacity":1,"blur":0,"isLocked":false,"gid":24,"smootheningRate":0,"initialPoint":[-167.96486361526627,-465.50590420624349],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(oval)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.87340185278117,497.83330507205574],"reflectionModeOverride":0,"anchorPoint":[292.87340185278117,539.25437383096278],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[292.87340185278117,580.67544258986891],"nextPoint":[-167.96486361526627,-215.50755823513555]},{"outPoint":[409.29399522804465,464.25484921460566],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,464.25484921460566],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[326.45185771023148,464.25484921460566],"nextPoint":[-167.96486361526627,-215.50755823513555]},{"outPoint":[442.87240412029564,580.67544258986891],"reflectionModeOverride":0,"anchorPoint":[442.87240412029564,539.25437383096278],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[442.87240412029564,497.83330507205574],"nextPoint":[-167.96486361526627,-215.50755823513555]},{"outPoint":[326.45185771023148,614.2538514821199],"reflectionModeOverride":0,"anchorPoint":[367.87292646913806,614.2538514821199],"cornerRadius":0,"prevPoint":[-167.96486361526627,-215.50755823513555],"inPoint":[409.29399522804465,614.2538514821199],"nextPoint":[-167.96486361526627,-215.50755823513555]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-167.96486361526627,-215.50755823513555],"opacity":1,"blur":0,"isLocked":false,"gid":25,"smootheningRate":0,"initialPoint":[-167.96486361526627,-215.50755823513555],"creationPoints":[],"name":"(oval)"},{"elementDescription":"(rectangle)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[302.87333569162547,289.07321462503751],"reflectionModeOverride":0,"anchorPoint":[302.87333569162547,289.07321462503751],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[302.87333569162547,289.07321462503751],"nextPoint":[105.33036558060121,-881.41965770385173]},{"outPoint":[432.87247428821752,289.07321462503751],"reflectionModeOverride":0,"anchorPoint":[432.87247428821752,289.07321462503751],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[432.87247428821752,289.07321462503751],"nextPoint":[105.33036558060121,-881.41965770385173]},{"outPoint":[432.87247428821752,539.07156338098093],"reflectionModeOverride":0,"anchorPoint":[432.87247428821752,539.07156338098093],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[432.87247428821752,539.07156338098093],"nextPoint":[105.33036558060121,-881.41965770385173]},{"outPoint":[302.87333569162547,539.07156338098093],"reflectionModeOverride":0,"anchorPoint":[302.87333569162547,539.07156338098093],"cornerRadius":0,"prevPoint":[105.33036558060121,-881.41965770385173],"inPoint":[302.87333569162547,539.07156338098093],"nextPoint":[105.33036558060121,-881.41965770385173]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[432.87247428821752,539.07156338098093],"opacity":1,"blur":0,"isLocked":false,"gid":26,"smootheningRate":0,"initialPoint":[302.87333569162547,289.07321462503751],"creationPoints":[],"name":"(rectangle)"}]},"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[103.16114089391976,309.16721333385931],"reflectionModeOverride":0,"anchorPoint":[103.16114089391976,309.16721333385931],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[103.16114089391976,309.16721333385931],"nextPoint":[84.093506586570356,146.69454513811797]},{"outPoint":[226.95560052710721,443.90288627635027],"reflectionModeOverride":0,"anchorPoint":[226.95560052710721,443.90288627635027],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[226.95560052710721,443.90288627635027],"nextPoint":[84.093506586570356,146.69454513811797]},{"outPoint":[366.67124423505425,309.6035637643763],"reflectionModeOverride":0,"anchorPoint":[366.67124423505425,309.6035637643763],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[366.67124423505425,309.6035637643763],"nextPoint":[84.093506586570356,146.69454513811797]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[84.093506586570356,146.69454513811797],"opacity":1,"blur":0,"isLocked":false,"gid":29,"smootheningRate":0,"initialPoint":[84.093506586570356,146.69454513811797],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[383.41128768868771,307.07623767819223],"reflectionModeOverride":0,"anchorPoint":[383.41128768868771,307.07623767819223],"cornerRadius":0,"prevPoint":[364.08468148613224,146.65290478922907],"inPoint":[383.41128768868771,307.07623767819223],"nextPoint":[364.08468148613224,146.65290478922907]},{"outPoint":[508.88709306778787,440.11243356580462],"reflectionModeOverride":0,"anchorPoint":[508.88709306778787,440.11243356580462],"cornerRadius":0,"prevPoint":[364.08468148613224,146.65290478922907],"inPoint":[508.88709306778787,440.11243356580462],"nextPoint":[364.08468148613224,146.65290478922907]},{"outPoint":[650.50032010766949,307.50708423933065],"reflectionModeOverride":0,"anchorPoint":[650.50032010766949,307.50708423933065],"cornerRadius":0,"prevPoint":[364.08468148613224,146.65290478922907],"inPoint":[650.50032010766949,307.50708423933065],"nextPoint":[364.08468148613224,146.65290478922907]}],"closed":true,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[364.08468148613224,146.65290478922907],"opacity":1,"blur":0,"isLocked":false,"gid":30,"smootheningRate":0,"initialPoint":[364.08468148613224,146.65290478922907],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[522.04458359486512,154.80813254227905],"reflectionModeOverride":0,"anchorPoint":[522.04458359486512,154.80813254227905],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[522.04458359486512,154.80813254227905],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[229.5593561025587,448.88181029919406],"reflectionModeOverride":0,"anchorPoint":[229.5593561025587,448.88181029919406],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[229.5593561025587,448.88181029919406],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[83.839739125688993,300.1041732651538],"reflectionModeOverride":0,"anchorPoint":[83.839739125688993,300.1041732651538],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[83.839739125688993,300.1041732651538],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[653.66624413744626,302.9355554780409],"reflectionModeOverride":0,"anchorPoint":[653.66624413744626,302.9355554780409],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[653.66624413744626,302.9355554780409],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[511.75868688143851,444.7847840681311],"reflectionModeOverride":0,"anchorPoint":[511.75868688143851,444.7847840681311],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[511.75868688143851,444.7847840681311],"nextPoint":[1039.3203857761648,-115.61701281386331]},{"outPoint":[223.10077860149863,156.11368443580113],"reflectionModeOverride":0,"anchorPoint":[223.10077860149863,156.11368443580113],"cornerRadius":0,"prevPoint":[1039.3203857761648,-115.61701281386331],"inPoint":[223.10077860149863,156.11368443580113],"nextPoint":[1039.3203857761648,-115.61701281386331]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":90,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[1200.0148931967549,-113.13323396127316],"opacity":1,"blur":0,"isLocked":false,"gid":21,"smootheningRate":0,"initialPoint":[1200.0148931967549,-113.13323396127316],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[295.85728153799414,418.73751961026619],"reflectionModeOverride":0,"anchorPoint":[295.85728153799414,418.73751961026619],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[295.85728153799414,418.73751961026619],"nextPoint":[84.093506586570356,146.69454513811797]},{"outPoint":[438.89979217720088,418.73751961026619],"reflectionModeOverride":0,"anchorPoint":[438.89979217720088,418.73751961026619],"cornerRadius":0,"prevPoint":[84.093506586570356,146.69454513811797],"inPoint":[438.89979217720088,418.73751961026619],"nextPoint":[84.093506586570356,146.69454513811797]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[84.093506586570356,146.69454513811797],"opacity":1,"blur":0,"isLocked":false,"gid":31,"smootheningRate":0,"initialPoint":[84.093506586570356,146.69454513811797],"creationPoints":[],"name":"(line)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[295.85728153799414,521.73683815036247],"reflectionModeOverride":0,"anchorPoint":[295.85728153799414,521.73683815036247],"cornerRadius":0,"prevPoint":[84.093506586570356,249.69386367821471],"inPoint":[295.85728153799414,521.73683815036247],"nextPoint":[84.093506586570356,249.69386367821471]},{"outPoint":[438.89979217720088,521.73683815036247],"reflectionModeOverride":0,"anchorPoint":[438.89979217720088,521.73683815036247],"cornerRadius":0,"prevPoint":[84.093506586570356,249.69386367821471],"inPoint":[438.89979217720088,521.73683815036247],"nextPoint":[84.093506586570356,249.69386367821471]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":61.869819641113281,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[84.093506586570356,249.69386367821471],"opacity":1,"blur":0,"isLocked":false,"gid":32,"smootheningRate":0,"initialPoint":[84.093506586570356,249.69386367821471],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[70.109671854888575,134.69462453150436],"opacity":1,"blur":0,"isLocked":false,"gid":55,"smootheningRate":0,"initialPoint":[70.109671854888575,134.69462453150436],"creationPoints":[],"group":{"elements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.7690644381571,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.7690644381571,667.02363476289884],"cornerRadius":0,"prevPoint":[72.193980608055426,108.16792868331186],"inPoint":[368.26333755475412,638.68961321030247],"nextPoint":[72.193980608055426,108.16792868331186]},{"outPoint":[367.82059343747073,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.82059343747073,616.38299074283532],"cornerRadius":0,"prevPoint":[55.02340366515682,81.837967291336099],"inPoint":[367.82059343747073,616.38299074283532],"nextPoint":[55.02340366515682,81.837967291336099]},{"outPoint":[383.13716259159514,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[403.6767241609266,616.33647011725679],"cornerRadius":0,"prevPoint":[70.109671854888575,134.1591558035534],"inPoint":[403.6767241609266,616.33647011725679],"nextPoint":[70.109671854888575,134.1591558035534]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[72.357622652088821,121.79167358010636],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[72.357622652088821,121.79167358010636],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.83565713463508,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.83565713463508,667.02363476289884],"cornerRadius":0,"prevPoint":[663.41074096473653,108.16792868331186],"inPoint":[367.34138401803807,638.68961321030247],"nextPoint":[663.41074096473653,108.16792868331186]},{"outPoint":[367.78412813532145,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.78412813532145,616.38299074283532],"cornerRadius":0,"prevPoint":[680.58131790763514,81.837967291336099],"inPoint":[367.78412813532145,616.38299074283532],"nextPoint":[680.58131790763514,81.837967291336099]},{"outPoint":[352.46755898119659,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[331.92799741186559,616.33647011725679],"cornerRadius":0,"prevPoint":[665.49504971790361,134.1591558035534],"inPoint":[331.92799741186559,616.33647011725679],"nextPoint":[665.49504971790361,134.1591558035534]}],"closed":true,"reversed":false}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[663.24709892070291,121.99906937808692],"opacity":1,"blur":0,"isLocked":false,"gid":54,"smootheningRate":0,"initialPoint":[663.24709892070291,121.99906937808692],"creationPoints":[],"name":"(curve)"}]},"name":"(curve)"}]},"name":"(curve)"}],"isExpanded":false,"isLocked":false,"isVisible":true,"opacity":1,"gid":4,"name":"Layer 1"}],"frame":{"y":0,"x":0,"width":745,"height":744.6337699943515},"title":"Mac App icon","activeLayerIndex":0,"settings":{"gridSpacing":20,"gridAngle":45,"backgroundColor":{"b":1,"s":0,"h":0,"a":1},"gridMode":0,"isGridVisible":false},"guideLayer":{"isExpanded":false,"elements":[],"isLocked":false,"defaultName":"Guides","isVisible":true,"opacity":1,"name":"Guides","gid":5},"gid":3} diff --git a/docs/images/logo_framed.vectornator/Document.json b/docs/images/logo_framed.vectornator/Document.json index 974df2c..a2f614f 100644 --- a/docs/images/logo_framed.vectornator/Document.json +++ b/docs/images/logo_framed.vectornator/Document.json @@ -1 +1 @@ -{"date":644900741.09290397,"appVersion":"4.1.5","drawing":{"modificationDate":644894800.328192,"activeArtboardIndex":0,"settings":{"outlineMode":false,"isolateActiveLayer":false,"snapToEdges":false,"snapToPoints":false,"guidesVisible":true,"snapToGrid":false,"units":"Pixels","dimensionsVisible":true,"dynamicGuides":false,"isCMYKColorPreviewEnabled":false,"undoHistoryDisabled":false,"snapToGuides":true,"drawOnlyUsingPencil":false,"whiteBackground":false,"rulersVisible":true,"isTimeLapseWatermarkDisabled":false},"artboardPaths":["Artboard0.json"],"documentVersion":"unknown"}}
\ No newline at end of file +{"date":644900741.09290397,"appVersion":"4.1.5","drawing":{"modificationDate":644894800.328192,"activeArtboardIndex":0,"settings":{"outlineMode":false,"isolateActiveLayer":false,"snapToEdges":false,"snapToPoints":false,"guidesVisible":true,"snapToGrid":false,"units":"Pixels","dimensionsVisible":true,"dynamicGuides":false,"isCMYKColorPreviewEnabled":false,"undoHistoryDisabled":false,"snapToGuides":true,"drawOnlyUsingPencil":false,"whiteBackground":false,"rulersVisible":true,"isTimeLapseWatermarkDisabled":false},"artboardPaths":["Artboard0.json"],"documentVersion":"unknown"}} diff --git a/docs/images/logo_framed.vectornator/Manifest.json b/docs/images/logo_framed.vectornator/Manifest.json index 0f80b78..8e897a0 100644 --- a/docs/images/logo_framed.vectornator/Manifest.json +++ b/docs/images/logo_framed.vectornator/Manifest.json @@ -1 +1 @@ -{"documentJSONFilename":"Document.json","undoHistoryJSONFilename":"UndoHistory.json","fileFormatVersion":0,"thumbnailImageFilename":"Thumbnail.png"}
\ No newline at end of file +{"documentJSONFilename":"Document.json","undoHistoryJSONFilename":"UndoHistory.json","fileFormatVersion":0,"thumbnailImageFilename":"Thumbnail.png"} diff --git a/docs/images/logo_framed.vectornator/UndoHistory.json b/docs/images/logo_framed.vectornator/UndoHistory.json index 0238dd1..b0f4385 100644 --- a/docs/images/logo_framed.vectornator/UndoHistory.json +++ b/docs/images/logo_framed.vectornator/UndoHistory.json @@ -1 +1 @@ -{"cacheElements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-17.790763092914176,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[-17.790763092914176,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-17.790763092914176,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-118.14742861878335,355.38893101990755],"reflectionModeOverride":0,"anchorPoint":[-118.14742861878335,355.38893101990755],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-118.14742861878335,355.38893101990755],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-89.082653539453304,326.03057263672883],"reflectionModeOverride":0,"anchorPoint":[-89.082653539453304,326.03057263672883],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-89.082653539453304,326.03057263672883],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,406.22781863362536],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,406.22781863362536],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,406.22781863362536],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,214.66453204993843],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,214.66453204993843],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,214.66453204993843],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,359.98840044701058],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,359.98840044701058],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,359.98840044701058],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[40.289832016581158,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[40.289832016581158,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[40.289832016581158,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,551.89420905136228],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,551.89420905136228],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,551.89420905136228],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,697.21808678120522],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,697.21808678120522],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,697.21808678120522],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,505.65480953028907],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,505.65480953028907],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,505.65480953028907],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-88.78907023560464,585.60738760822585],"reflectionModeOverride":0,"anchorPoint":[-88.78907023560464,585.60738760822585],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-88.78907023560464,585.60738760822585],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-117.85384531493469,556.24902922504714],"reflectionModeOverride":0,"anchorPoint":[-117.85384531493469,556.24902922504714],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-117.85384531493469,556.24902922504714],"nextPoint":[-343.22817622315347,103.6410041510419]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-343.22817622315347,103.6410041510419],"opacity":1,"blur":0,"isLocked":false,"gid":13,"smootheningRate":0,"initialPoint":[-343.22817622315347,103.6410041510419],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-87.50742636811205,292.97453151588081],"reflectionModeOverride":0,"anchorPoint":[-87.50742636811205,292.97453151588081],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.50742636811205,292.97453151588081],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-78.027426825875665,283.48453174476265],"reflectionModeOverride":0,"anchorPoint":[-78.027426825875665,283.48453174476265],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-78.027426825875665,283.48453174476265],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-87.507426368111936,274.01453147773384],"reflectionModeOverride":0,"anchorPoint":[-87.507426368111936,274.01453147773384],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.507426368111936,274.01453147773384],"nextPoint":[-164.14742861878338,191.97453151588081]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588081],"opacity":1,"blur":0,"isLocked":false,"gid":14,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588081],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":11,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[340,700],"reflectionModeOverride":0,"anchorPoint":[340,660],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[340,620],"nextPoint":[0,0]},{"outPoint":[340,600],"reflectionModeOverride":0,"anchorPoint":[380,600],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[420,600],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":35,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.67257107604084,316.56452785377144],"reflectionModeOverride":0,"anchorPoint":[221.67257107604084,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257107604084,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.78002147975192,316.58196755365179],"reflectionModeOverride":0,"anchorPoint":[222.50257105935154,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.50257105935154,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.46754319256974,316.15798118973765],"reflectionModeOverride":0,"anchorPoint":[223.27257099819641,316.3245278149393],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.05416436926282,316.49652037792839],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.56257095468965,315.05452779244973],"reflectionModeOverride":0,"anchorPoint":[223.56257095468965,315.65452781629159],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.57458948209043,315.9106673969701],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.50257101191011,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[222.50257101191011,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.18257095945802,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257102859941,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[221.67257102859941,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257102859941,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":7,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[223.39257110465107,318.92452774886726],"reflectionModeOverride":0,"anchorPoint":[223.39257110465107,318.92452774886726],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.39257110465107,318.92452774886726],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.33257116187153,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[222.33257116187153,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.33257116187153,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,314.29453168754219],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.85351610118363,314.2910670771638],"reflectionModeOverride":0,"anchorPoint":[222.64257116425571,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.64257116425571,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.44257068590508,314.46434348341825],"reflectionModeOverride":0,"anchorPoint":[223.26257121864279,314.39453162176318],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.06341253016012,314.32492145971571],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.8867055322342,314.82297152263595],"reflectionModeOverride":0,"anchorPoint":[223.75257125112995,314.69453165532451],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.60854628757448,314.56596123813961],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.15455094852729,315.31612333025106],"reflectionModeOverride":0,"anchorPoint":[224.07257117822328,315.14453165999419],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.99528738469252,314.97566482247669],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.19825083117667,315.9922372888982],"reflectionModeOverride":0,"anchorPoint":[224.19257122020886,315.69453168706491],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.19562660185056,315.50438689028209],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.7338072115908,316.75555373263109],"reflectionModeOverride":0,"anchorPoint":[223.91257119347284,316.51453168318102],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.09914577717603,316.28247388344346],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.19257120773227,317.01453168185151],"reflectionModeOverride":0,"anchorPoint":[223.19257120773227,317.01453168185151],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.48085346525613,316.9312160768892],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.99257120475204,317.08453168214953],"reflectionModeOverride":0,"anchorPoint":[222.99257120475204,317.08453168214953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.99257120475204,317.08453168214953],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.0925712285939,318.97453166784442],"reflectionModeOverride":0,"anchorPoint":[224.0925712285939,318.97453166784442],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.0925712285939,318.97453166784442],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":8,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.88165496610824,312.75214941635204],"reflectionModeOverride":0,"anchorPoint":[222.39257110465107,312.75452767257332],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257110465107,312.75452767257332],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.45378029794426,313.23854645592189],"reflectionModeOverride":0,"anchorPoint":[220.90257124445034,313.04452773196982],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.37529974656684,312.85070177480884],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.35876943156495,314.2208600575953],"reflectionModeOverride":0,"anchorPoint":[219.70257152159786,313.86452769906913],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.04640821090715,313.51691728007791],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.51848057480817,316.05954140545026],"reflectionModeOverride":0,"anchorPoint":[218.89257170245423,315.09452765536599],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.0841152815558,314.63792794299417],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.07885049250976,318.55391401613247],"reflectionModeOverride":0,"anchorPoint":[218.89257166857527,318.09452771720584],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.51848023468915,317.12951359608263],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.04640881460426,319.67213836278347],"reflectionModeOverride":0,"anchorPoint":[219.70257171508007,319.32452783266035],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.35412998721458,318.97193143333453],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.32887552639454,320.75534631866788],"reflectionModeOverride":0,"anchorPoint":[220.90257185775954,320.14452762388811],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.45378073087409,319.9505090873252],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.42747093525361,318.97720769048311],"reflectionModeOverride":0,"anchorPoint":[225.07257197531021,319.32452740981233],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.98375014972524,320.42992729161517],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25666363710593,317.12951355179308],"reflectionModeOverride":0,"anchorPoint":[225.8825720147413,318.09452742555698],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.70369149262439,318.55776181404121],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.69870344883279,314.63387871118448],"reflectionModeOverride":0,"anchorPoint":[225.88257204862026,315.09452736371713],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.25666342066654,316.05954129932087],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.73054756988128,313.51475021332715],"reflectionModeOverride":0,"anchorPoint":[225.07257201185996,313.86452742080394],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.42312528062655,314.21540813724465],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.40302999066401,312.851739523118],"reflectionModeOverride":0,"anchorPoint":[223.87257194075443,313.04452740721484],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.32272320141846,313.23607014436448],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.39257194847221,312.754527354262],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,312.754527354262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.90014945704277,312.7532021443493],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":9,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.79793545229796,321.06845412941806],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,321.06452777387869],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257194847221,321.06452777387869],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.13630955687603,320.47368746402753],"reflectionModeOverride":0,"anchorPoint":[220.66257205810552,320.71452796827054],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.20891937561655,320.94928924957026],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.84008207185138,319.31458146286332],"reflectionModeOverride":0,"anchorPoint":[219.26257159607349,319.71452784560978],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.66107353858615,320.13423302542299],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[217.79637500761001,317.20177600727493],"reflectionModeOverride":0,"anchorPoint":[218.26257166453621,318.30452834140976],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.50031051375157,318.83550383670706],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.71657507260346,313.79480682341318],"reflectionModeOverride":0,"anchorPoint":[218.26257150938892,314.85452883246262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[217.79637484257967,315.95728089190948],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.15964890699888,312.25319776201661],"reflectionModeOverride":0,"anchorPoint":[220.61257125382446,312.48452913388269],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.5567350056233,312.94749659064405],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.93948006388655,312.14028017121217],"reflectionModeOverride":0,"anchorPoint":[222.34257146546875,312.14452894709979],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.74863705025209,312.1374427227563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.61118094730551,312.72639164873863],"reflectionModeOverride":0,"anchorPoint":[224.08257153232208,312.48452915920495],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.53116831317226,312.25589748063879],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.90916737355408,313.88955714821628],"reflectionModeOverride":0,"anchorPoint":[225.49257153005306,313.48452928232075],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.08951592668194,313.06563634089633],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.95876793869749,315.99728178436908],"reflectionModeOverride":0,"anchorPoint":[226.49257128177129,314.89452945023424],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.24808810980261,314.36743571685838],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25070868333646,318.87313890226199],"reflectionModeOverride":0,"anchorPoint":[226.49257117287002,318.34452948727852],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.95876813673391,317.24177716378313],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.65955409863321,320.59420064219239],"reflectionModeOverride":0,"anchorPoint":[225.49257104975428,319.75452948500953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.91146402567489,319.35147381264608],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.34257072457945,321.06452950412722],"reflectionModeOverride":0,"anchorPoint":[222.34257072457945,321.06452950412722],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.52534831488276,321.06588609892162],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":10,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":6,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.2196979967656,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[270.70254252087443,481.42866614307741],"cornerRadius":0,"prevPoint":[0,1.3634276556741725],"inPoint":[246.45876716860445,474.78563603599895],"nextPoint":[0,1.3634276556741725]},{"outPoint":[308.32019835237998,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.80961189523731,532.13830093176216],"cornerRadius":0,"prevPoint":[9.9345758586960642,16.47175369678871],"inPoint":[307.29902543809465,503.80404406807986],"nextPoint":[9.9345758586960642,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[9.9345758586960642,15.108326041114537],"opacity":1,"blur":0,"isLocked":false,"gid":44,"smootheningRate":0,"initialPoint":[9.9345758586960642,15.108326041114537],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[271.39702488697174,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[345.33038784778722,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,547.81167390718997],"reflectionModeOverride":0,"anchorPoint":[308.36370636737951,547.81167390718997],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,547.81167390718997],"nextPoint":[86.563617484933189,-170.37985441312253]}],"closed":true,"reversed":true}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[579.21777967228218,-165.50118646031109],"opacity":1,"blur":0,"isLocked":false,"gid":37,"smootheningRate":0,"initialPoint":[579.21777967228218,-165.50118646031109],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[307.98765662154068,497.22766759942954],"reflectionModeOverride":0,"anchorPoint":[307.98765662154068,526.40806463936553],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[307.98765662154068,555.58846167930153],"nextPoint":[0,0]},{"outPoint":[347.07124677007465,468.5633046399771],"reflectionModeOverride":0,"anchorPoint":[331.56894097295356,478.9361833057668],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[316.06663517583246,489.3090619715565],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":33,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.7690644381571,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.7690644381571,667.02363476289884],"cornerRadius":0,"prevPoint":[72.193980608055426,108.16792868331186],"inPoint":[368.26333755475412,638.68961321030247],"nextPoint":[72.193980608055426,108.16792868331186]},{"outPoint":[367.82059343747073,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.82059343747073,616.38299074283532],"cornerRadius":0,"prevPoint":[55.02340366515682,81.837967291336099],"inPoint":[367.82059343747073,616.38299074283532],"nextPoint":[55.02340366515682,81.837967291336099]},{"outPoint":[383.13716259159514,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[403.6767241609266,616.33647011725679],"cornerRadius":0,"prevPoint":[70.109671854888575,134.1591558035534],"inPoint":[403.6767241609266,616.33647011725679],"nextPoint":[70.109671854888575,134.1591558035534]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[72.357622652088821,121.79167358010636],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[72.357622652088821,121.79167358010636],"creationPoints":[],"name":"(curve)"}]}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-2.4740749822735211,26.734403557988003],"opacity":1,"blur":0,"isLocked":false,"gid":46,"smootheningRate":0,"initialPoint":[-2.4740749822735211,26.734403557988003],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":1,"s":0,"h":0,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[189.49257363188801,253.97453151588081],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,253.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,253.97453151588081],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[198.97257317412434,244.48453174476265],"reflectionModeOverride":0,"anchorPoint":[198.97257317412434,244.48453174476265],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[198.97257317412434,244.48453174476265],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[189.49257363188801,235.01453147773384],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,235.01453147773384],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,235.01453147773384],"nextPoint":[112.85257138121662,152.97453151588078]}],"closed":true,"reversed":false}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,152.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":16,"smootheningRate":0,"initialPoint":[112.85257138121662,152.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[323.38754763944246,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[344.90470311533363,481.42866614307741],"cornerRadius":0,"prevPoint":[615.60724563620806,1.3634276556741725],"inPoint":[369.14847846760358,474.78563603599895],"nextPoint":[615.60724563620806,1.3634276556741725]},{"outPoint":[307.28704728382809,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.79763374097075,532.13830093176216],"cornerRadius":0,"prevPoint":[605.67266977751206,16.47175369678871],"inPoint":[308.30822019811342,503.80404406807986],"nextPoint":[605.67266977751206,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[605.67266977751206,16.47175369678871],"opacity":1,"blur":0,"isLocked":false,"gid":45,"smootheningRate":0,"initialPoint":[605.67266977751206,16.47175369678871],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[13.974435927889729,160.80268978822852],"reflectionModeOverride":0,"anchorPoint":[13.974435927889729,160.80268978822852],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[13.974435927889729,160.80268978822852],"nextPoint":[0,0]},{"outPoint":[144.25556161242727,291.78142890790133],"reflectionModeOverride":0,"anchorPoint":[144.25556161242727,291.78142890790133],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[144.25556161242727,291.78142890790133],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":28,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[320,664.0506297595],"reflectionModeOverride":0,"anchorPoint":[320,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[320,580],"nextPoint":[0,0]},{"outPoint":[344.050612449646,595.89893977911322],"reflectionModeOverride":0,"anchorPoint":[337.34381007033926,612.48211711168926],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[327.23039949003834,637.48844227310212],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[360,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[352.025306224823,580],"nextPoint":[0,0]},{"outPoint":[260,580],"reflectionModeOverride":0,"anchorPoint":[280,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":true,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":34,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300,640],"reflectionModeOverride":0,"anchorPoint":[300,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,640],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[340,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":36,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"}],"cacheLayers":[],"cacheArtboardPaths":[],"undoStack":[[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11,6]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -29.209948435277369, -232.41660739687057]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14,15]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.01450315572447696,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93007444840156717,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93451308072143913,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93910701763028559,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94055809677531532,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94999337125602745,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95297621548706413,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95790068402842177,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.011044777045815676,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96266052064980523,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0088583736096398309,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96911575815444173,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0077646546444650423,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97217928832645584,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0032726223185911016,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98016728703982747,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0010851843882415254,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98662352066719217,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99307006473711057,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.20437110010339629, -0, -0, 0.20437110010339629, 126.38769661378409, 170.79330548171168]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[13]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":2,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":186.05023255601679,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":558.85414169234264,\"OutPoint_X\":186.05023255601679,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":558.85414169234264},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":1,\"NodePoints\":{\"inPoint_X\":235.46249740746129,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":509.44413050490067,\"OutPoint_X\":136.6379677045723,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":608.26415287978466},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.03408561318607653,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.029926881951800856,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.021585238181938561,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.012961911164688478,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.011895648503707629,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.007872629590133573,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.005957393322960804,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-4.3711390001862419e-08, -0.99999999999999922, 0.99999999999999922, -4.3711390001862419e-08, -173.29768953296772, 744.95852622656867]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 8.2190355245806472, 176.90598115888827]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.55538688884063114, -0, 182.49084631957811]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 150.32620239929324, 36.081797695525552]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12249755187863798, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.64978701032960984, -0, -0, 0.64978701032960984, 52.531948450558538, 142.05406077476505]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.171276915352394, 196.69205141995428]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.65616231221472443, -0, -0, 0.65616231221472443, 51.425334356867019, 141.12771095949628]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.53221494113933, 129.60740108742715]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.17127691535239, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.09503537570964, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 208.92985571850454]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 130.84123782858848]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 75]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 4.5418914390142788, -306.56258907512347]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.96223302635797447, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0392493219135692, -0, -0, 1.0392493219135692, 0, -2.9436991435176969]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 15.458108560985721, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 26.562589075123469]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 50]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":22}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 9.881426012744555, 0.91199734232554874]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -77.745954011856824, 28.270796276850916]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 67.864527999112269, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.1538461432654479, -0, -0, 1, -1.5384614326544779, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -136.98574299882625, 7.8612196478959504]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -53.141264363887032, -43.604121250556744]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -70.052055171480333, -2.5049969673447094]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 219.77475934149726, 163.57319520938375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.6846814845770105, -0, -0, 0.6846814845770105, -0.080018108436871196, 3.1890182924268196]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -85.587957051898314, -21.453286888159028]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":142.86303913891427,\"AnchorPoint_Y\":297.21030751597522,\"inPoint_Y\":297.21030751597522,\"OutPoint_X\":142.86303913891427,\"AnchorPoint_X\":142.86303913891427,\"OutPoint_Y\":297.21030751597522},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -258.8309537310077, 14.790477788784642]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.98660023945785613, -0, -0, 1.0127745463822073, 7.5897658853896965, -3.7483052243093384]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":307.98765662154068,\"AnchorPoint_Y\":526.40806463936553,\"inPoint_Y\":555.58846167930153,\"OutPoint_X\":307.98765662154068,\"AnchorPoint_X\":307.98765662154068,\"OutPoint_Y\":497.22766759942954},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":9223372036854775807}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":580,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":680.00002878904479},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":660,\"inPoint_Y\":620,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":700},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 20, 20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 640, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 39, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":220,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 60, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{540, 0}\",\"VNPrevPointForRadiusKey\":\"{540, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":300,\"AnchorPoint_X\":280,\"OutPoint_Y\":600},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[],"methodSignature":"reversePathDirection","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":600,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":240,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -60, 100]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -8.3360061810856791, -0.18596575427056905]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12624915594960839, -0.84513498021431133]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0820554726071352, -0, -0, 1, -28.572516179113212, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 3.7693360211719664, 0.58007166688469169]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.8911271920692454, -0, 58.840421083655528]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0.018101184397323777, -6.1834757589472247]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.5616683932951787, -14.721591768371582]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.4335340527209723, 13.544422687729025]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":43}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.10440918421369,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":598.11903819000008},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":366.0442444460744,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.21462016330969},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":337.32713106884489,\"AnchorPoint_Y\":505.14299981511687,\"inPoint_Y\":505.14299981511687,\"OutPoint_X\":337.32713106884489,\"AnchorPoint_X\":337.32713106884489,\"OutPoint_Y\":505.14299981511687},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":282.38947067174871,\"AnchorPoint_Y\":502.68576551434728,\"inPoint_Y\":502.68576551434728,\"OutPoint_X\":282.38947067174871,\"AnchorPoint_X\":282.38947067174871,\"OutPoint_Y\":502.68576551434728},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"VNPrevPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":303.99512941310888,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":487.18557523329599,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":45}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 618.5121544161118, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -17.095091220096265, 18.636572344325828]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 2.4740749822735211, -26.734403557988003]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[46]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":50}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":50}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":323.12940602919167,\"AnchorPoint_Y\":534.54856008655031,\"inPoint_Y\":534.54856008655031,\"OutPoint_X\":323.12940602919167,\"AnchorPoint_X\":323.12940602919167,\"OutPoint_Y\":534.54856008655031},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[47,48]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":46},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13822251558303833,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -2.1531569701203352, 25.991399082341331]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":308.00218177519929,\"AnchorPoint_Y\":532.86800447564667,\"inPoint_Y\":504.53379546064855,\"OutPoint_X\":307.49158198784681,\"AnchorPoint_X\":307.49158198784681,\"OutPoint_Y\":532.86800447564667},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.54481307650724,\"AnchorPoint_Y\":482.22702540901258,\"inPoint_Y\":482.22702540901258,\"OutPoint_X\":307.54481307650724,\"AnchorPoint_X\":307.54481307650724,\"OutPoint_Y\":482.22702540901258},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"VNPrevPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":344.58533198784681,\"AnchorPoint_Y\":482.18050447564667,\"inPoint_Y\":482.18050447564667,\"OutPoint_X\":323.06817538141632,\"AnchorPoint_X\":344.58533198784681,\"OutPoint_Y\":488.07641427863712},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.34003453224391933]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.1954377384541317]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 692.07691397569363, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 57.024957634971202, 19.792602829855639]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":307.04977456552314,\"AnchorPoint_Y\":532.53992937509292,\"inPoint_Y\":504.20572036009486,\"OutPoint_X\":307.56037435287561,\"AnchorPoint_X\":307.56037435287561,\"OutPoint_Y\":532.53992937509292},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"VNPrevPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.50714326421519,\"AnchorPoint_Y\":481.89895030845889,\"inPoint_Y\":481.89895030845889,\"OutPoint_X\":307.50714326421519,\"AnchorPoint_X\":307.50714326421519,\"OutPoint_Y\":481.89895030845889},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"VNPrevPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":270.46662435287561,\"AnchorPoint_Y\":481.85242937509298,\"inPoint_Y\":481.85242937509298,\"OutPoint_X\":291.68464211968808,\"AnchorPoint_X\":270.46662435287561,\"OutPoint_Y\":491.96348695323337},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"VNPrevPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":55},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0330248439984473, -0, -0, 0.99999999999999989, -11.379876833051835, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[55]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":56},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":55}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":57}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -290.115478515625, 62.302734375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.89467432944640213,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.90097236067321962,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.031593452065677957,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.91739226661025597,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.019517801575741518,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93616537665754818,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0091232041181144082,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94328004740819738,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0018620895127118644,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95006183205443251,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95531556443573817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9587083584477003,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96669617604784863,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96977202191904677,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97283138648692502,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97731792325435829,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9818318081185089,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9863455118694362,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99094089768406901,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99553628349870182,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -138.240478515625, -358.288818359375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10.2197265625, 7.887451171875]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.73681640625, -1.3956298828125]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96954748896640086,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96362693977790437,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.95841712343394081,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.954131593465262,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.93934857275056949,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.83357840083997725,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.80253639308086555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.76741239856207288,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.7565612097807517,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.73508462058656032,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.69529915290432798,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.68285743522209563,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.66214385499715267,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.64970324957289294,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.63305608449601369,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.62061547907175396,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.60169597095671978,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.59555519468963558,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5870419721668565,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.57588046341116172,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.56733832218109337,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5457271497722096,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.51220146996013671,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.45939702270785876,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.41278562784738043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.38011749893223234,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.35006206399487472,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.33336373505125283,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31040673049544421,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29577720138097952,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.27499243664578588,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.25639993237471526,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24782776017938496,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23669294561503418,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22722206897779043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22344372864464693,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21991675861332574,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21874110193621868,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2197966347522779,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23113499252562641,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24180488325740318,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2529452591116173,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.28146132901480636,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29042946504840544,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29303437322038722,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30319262528473806,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31050016016514809,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31284924900341687,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31402379342255127,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31192273811218679,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31087054207004555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30975717183940776,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.3074892778331435,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0000066161593433, -0, -0, 1.0000066161593433, -0.00084115369224984499, -0.0015278888873493538]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 106.25150911801597, 215.10157288799655]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1024, 1024}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1018.1834573966, 1022.316502396953}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1008.5644489681886, 1015.9218488910001}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1003.3383988868811, 1012.188740268123}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {959.56182404754463, 968.37466824766148}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {930.99873639690077, 938.04865943448704}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {883.88375480634159, 898.86742705675647}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {851.39277451811745, 876.13749149913815}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {807.42735481491377, 847.80421878677203}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {780.8131139470363, 833.66058405713125}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {758.18676120653936, 823.6793880913026}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {745.42543866806363, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.45307691385142, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.65313908147255}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {735.7456294412641, 820.1359296408807}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {734.20024226104533, 819.105537302637}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {730.82720724054207, 815.73250228213374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {722.51078545309133, 807.41391622785272}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {715.22999050970566, 800.13291995732015}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {710.02528110597814, 794.3195482564854}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {708.99086222479468, 792.76802059828378}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {705.03881032957679, 786.83843280185465}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {703.71729893680003, 785.04591654871797}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {702.86608775936838, 784.19455437592615}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {701.16326275021129, 782.49188036212945}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {700.77188277648224, 782.10024872946656}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.97704320020512, 781.30550981676288}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.12865060283116, 780.87985389626033}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {698.70304501411556, 780.45414764397106}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {695.54100084364723, 776.8204946317021}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {693.70127337454369, 774.98056583545167}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {691.86235121402819, 772.67134345959084}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.97672115541332, 769.84279770809485}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.03491276184081, 768.90119064166925}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {686.67817717929256, 766.07264489017348}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {683.90469440249581, 763.76603976722345}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {682.53607245732564, 762.86897633206718}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {681.17026909221318, 762.39756881741346}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {678.95083862390538, 761.54620664462163}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {676.30580256688177, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.07408308165805, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.28025820206608, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {671.48603066818009, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.54341696601955, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.17600298373122, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.39103649860635, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.44842279644581, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.02281720772999, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.226367014277, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {663.57830298909857, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.7291050831368, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.92701772956843, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.54691207607016, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.69429082217562}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.30406847954168}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.91817467056762}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.52795232793369}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.12605301077474}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.73814593033103}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.96107347477482}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.97694686201953, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 757.18274272455005}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 756.35493582795493}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.94518475206382}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.5517915068649}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.78614436689986}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.3960730196261}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.00237778370661}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.60918586565458}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.21065877820774}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.81701387407497}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.41430924832821}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 749.69714774089903}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 748.93285955917622}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.99004452986878}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.13868235707696}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.41906127678772, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.65522608114543, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.27149653900142, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.88132452815421, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.48793128295529, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.69429966956, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.30453031300658, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.68503862079888, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.0695734715307, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.47263041978476, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.86843959074758, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.25498771294906, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.63670398362319, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.02566803158879, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.83158060094979, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {664.08383545516563, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.7862578100287, 746.7378903392264}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.06307457617595, 747.16359659151567}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.70851328749336, 748.01490843252077}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.98533005364084, 748.44061468481004}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.66399580516031, 748.84437627807847}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.94081257130756, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.19387273411121, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.58525270784025, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.20474440004818, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.93235683113426, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.1381292972485, 747.52296554887494}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.87500277709569, 747.09725929658589}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.47194582884163, 746.2471050866759}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.64368594616599, 745.41864387685314}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 745.02308636482417}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":330.67709661876142,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":247.83495910094825},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":214.25650324349817,\"inPoint_Y\":214.25650324349817,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":214.25650324349817},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":247.83495910094825,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":330.67709661876142},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":364.25550551101242,\"inPoint_Y\":364.25550551101242,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":364.25550551101242},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":580.67544258986891,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":497.83330507205574},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":464.25484921460566,\"inPoint_Y\":464.25484921460566,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":464.25484921460566},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":497.83330507205574,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":580.67544258986891},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":614.2538514821199,\"inPoint_Y\":614.2538514821199,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":614.2538514821199},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":97.57351080287799,\"AnchorPoint_Y\":309.16721333385931,\"inPoint_Y\":309.16721333385931,\"OutPoint_X\":97.57351080287799,\"AnchorPoint_X\":97.57351080287799,\"OutPoint_Y\":309.16721333385931},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":221.36797043606543,\"AnchorPoint_Y\":443.90288627635027,\"inPoint_Y\":443.90288627635027,\"OutPoint_X\":221.36797043606543,\"AnchorPoint_X\":221.36797043606543,\"OutPoint_Y\":443.90288627635027},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":361.08361414401247,\"AnchorPoint_Y\":309.6035637643763,\"inPoint_Y\":309.6035637643763,\"OutPoint_X\":361.08361414401247,\"AnchorPoint_X\":361.08361414401247,\"OutPoint_Y\":309.6035637643763},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":377.82365759764593,\"AnchorPoint_Y\":307.07623767819223,\"inPoint_Y\":307.07623767819223,\"OutPoint_X\":377.82365759764593,\"AnchorPoint_X\":377.82365759764593,\"OutPoint_Y\":307.07623767819223},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":503.2994629767461,\"AnchorPoint_Y\":440.11243356580462,\"inPoint_Y\":440.11243356580462,\"OutPoint_X\":503.2994629767461,\"AnchorPoint_X\":503.2994629767461,\"OutPoint_Y\":440.11243356580462},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":644.91269001662772,\"AnchorPoint_Y\":307.50708423933065,\"inPoint_Y\":307.50708423933065,\"OutPoint_X\":644.91269001662772,\"AnchorPoint_X\":644.91269001662772,\"OutPoint_Y\":307.50708423933065},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":516.45695350382334,\"AnchorPoint_Y\":154.80813254227905,\"inPoint_Y\":154.80813254227905,\"OutPoint_X\":516.45695350382334,\"AnchorPoint_X\":516.45695350382334,\"OutPoint_Y\":154.80813254227905},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":223.97172601151692,\"AnchorPoint_Y\":448.88181029919406,\"inPoint_Y\":448.88181029919406,\"OutPoint_X\":223.97172601151692,\"AnchorPoint_X\":223.97172601151692,\"OutPoint_Y\":448.88181029919406},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":78.252109034647219,\"AnchorPoint_Y\":300.1041732651538,\"inPoint_Y\":300.1041732651538,\"OutPoint_X\":78.252109034647219,\"AnchorPoint_X\":78.252109034647219,\"OutPoint_Y\":300.1041732651538},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":648.07861404640448,\"AnchorPoint_Y\":302.9355554780409,\"inPoint_Y\":302.9355554780409,\"OutPoint_X\":648.07861404640448,\"AnchorPoint_X\":648.07861404640448,\"OutPoint_Y\":302.9355554780409},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":506.17105679039673,\"AnchorPoint_Y\":444.7847840681311,\"inPoint_Y\":444.7847840681311,\"OutPoint_X\":506.17105679039673,\"AnchorPoint_X\":506.17105679039673,\"OutPoint_Y\":444.7847840681311},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":217.51314851045686,\"AnchorPoint_Y\":156.11368443580113,\"inPoint_Y\":156.11368443580113,\"OutPoint_X\":217.51314851045686,\"AnchorPoint_X\":217.51314851045686,\"OutPoint_Y\":156.11368443580113},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":362.67570746371234,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.18143434711533,\"AnchorPoint_X\":362.18143434711533,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.23296334642896,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.23296334642896,\"AnchorPoint_X\":362.23296334642896,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":398.08909406988482,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":377.54953250055337,\"AnchorPoint_X\":398.08909406988482,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":361.7537539269963,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.24802704359331,\"AnchorPoint_X\":362.24802704359331,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.19649804427968,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.19649804427968,\"AnchorPoint_X\":362.19649804427968,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":326.34036732082382,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":346.87992889015482,\"AnchorPoint_X\":326.34036732082382,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":\"Mac App icon\"}","argumentGID":0}],"methodSignature":"title","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {740, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {745, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}]],"isGroupingUndo":false,"currentUndoGroup":[],"currentRedoGroup":[],"isGroupingRedo":false,"redoStack":[],"nextGID":57}
\ No newline at end of file +{"cacheElements":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-17.790763092914176,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[-17.790763092914176,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-17.790763092914176,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-118.14742861878335,355.38893101990755],"reflectionModeOverride":0,"anchorPoint":[-118.14742861878335,355.38893101990755],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-118.14742861878335,355.38893101990755],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-89.082653539453304,326.03057263672883],"reflectionModeOverride":0,"anchorPoint":[-89.082653539453304,326.03057263672883],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-89.082653539453304,326.03057263672883],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,406.22781863362536],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,406.22781863362536],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,406.22781863362536],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0811227455444623,214.66453204993843],"reflectionModeOverride":0,"anchorPoint":[-9.0811227455444623,214.66453204993843],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0811227455444623,214.66453204993843],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,359.98840044701058],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,359.98840044701058],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,359.98840044701058],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[40.289832016581158,455.94130474918654],"reflectionModeOverride":0,"anchorPoint":[40.289832016581158,455.94130474918654],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[40.289832016581158,455.94130474918654],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[136.29167736876548,551.89420905136228],"reflectionModeOverride":0,"anchorPoint":[136.29167736876548,551.89420905136228],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[136.29167736876548,551.89420905136228],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,697.21808678120522],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,697.21808678120522],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,697.21808678120522],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-9.0322003610774573,505.65480953028907],"reflectionModeOverride":0,"anchorPoint":[-9.0322003610774573,505.65480953028907],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-9.0322003610774573,505.65480953028907],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-88.78907023560464,585.60738760822585],"reflectionModeOverride":0,"anchorPoint":[-88.78907023560464,585.60738760822585],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-88.78907023560464,585.60738760822585],"nextPoint":[-343.22817622315347,103.6410041510419]},{"outPoint":[-117.85384531493469,556.24902922504714],"reflectionModeOverride":0,"anchorPoint":[-117.85384531493469,556.24902922504714],"cornerRadius":0,"prevPoint":[-343.22817622315347,103.6410041510419],"inPoint":[-117.85384531493469,556.24902922504714],"nextPoint":[-343.22817622315347,103.6410041510419]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-343.22817622315347,103.6410041510419],"opacity":1,"blur":0,"isLocked":false,"gid":13,"smootheningRate":0,"initialPoint":[-343.22817622315347,103.6410041510419],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[-87.50742636811205,292.97453151588081],"reflectionModeOverride":0,"anchorPoint":[-87.50742636811205,292.97453151588081],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.50742636811205,292.97453151588081],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-78.027426825875665,283.48453174476265],"reflectionModeOverride":0,"anchorPoint":[-78.027426825875665,283.48453174476265],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-78.027426825875665,283.48453174476265],"nextPoint":[-164.14742861878338,191.97453151588081]},{"outPoint":[-87.507426368111936,274.01453147773384],"reflectionModeOverride":0,"anchorPoint":[-87.507426368111936,274.01453147773384],"cornerRadius":0,"prevPoint":[-164.14742861878338,191.97453151588081],"inPoint":[-87.507426368111936,274.01453147773384],"nextPoint":[-164.14742861878338,191.97453151588081]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588081],"opacity":1,"blur":0,"isLocked":false,"gid":14,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588081],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-164.14742861878338,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":11,"smootheningRate":0,"initialPoint":[-164.14742861878338,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[340,700],"reflectionModeOverride":0,"anchorPoint":[340,660],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[340,620],"nextPoint":[0,0]},{"outPoint":[340,600],"reflectionModeOverride":0,"anchorPoint":[380,600],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[420,600],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":35,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":0.98823529481887817,"s":1,"h":0.58068782582120682,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.67257107604084,316.56452785377144],"reflectionModeOverride":0,"anchorPoint":[221.67257107604084,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257107604084,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.78002147975192,316.58196755365179],"reflectionModeOverride":0,"anchorPoint":[222.50257105935154,316.56452785377144],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.50257105935154,316.56452785377144],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.46754319256974,316.15798118973765],"reflectionModeOverride":0,"anchorPoint":[223.27257099819641,316.3245278149393],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.05416436926282,316.49652037792839],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.56257095468965,315.05452779244973],"reflectionModeOverride":0,"anchorPoint":[223.56257095468965,315.65452781629159],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.57458948209043,315.9106673969701],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.50257101191011,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[222.50257101191011,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.18257095945802,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257102859941,314.72452780913903],"reflectionModeOverride":0,"anchorPoint":[221.67257102859941,314.72452780913903],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257102859941,314.72452780913903],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":7,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[223.39257110465107,318.92452774886726],"reflectionModeOverride":0,"anchorPoint":[223.39257110465107,318.92452774886726],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.39257110465107,318.92452774886726],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.33257116187153,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[222.33257116187153,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.33257116187153,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,317.04452775363563],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,317.04452775363563],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,317.04452775363563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.67257113564548,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.67257113564548,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.67257113564548,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,318.97453151588081],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,318.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,318.97453151588081],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.06257112134037,314.29453168754219],"reflectionModeOverride":0,"anchorPoint":[221.06257112134037,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.06257112134037,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.85351610118363,314.2910670771638],"reflectionModeOverride":0,"anchorPoint":[222.64257116425571,314.29453168754219],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.64257116425571,314.29453168754219],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.44257068590508,314.46434348341825],"reflectionModeOverride":0,"anchorPoint":[223.26257121864279,314.39453162176318],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.06341253016012,314.32492145971571],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.8867055322342,314.82297152263595],"reflectionModeOverride":0,"anchorPoint":[223.75257125112995,314.69453165532451],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.60854628757448,314.56596123813961],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.15455094852729,315.31612333025106],"reflectionModeOverride":0,"anchorPoint":[224.07257117822328,315.14453165999419],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.99528738469252,314.97566482247669],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.19825083117667,315.9922372888982],"reflectionModeOverride":0,"anchorPoint":[224.19257122020886,315.69453168706491],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.19562660185056,315.50438689028209],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.7338072115908,316.75555373263109],"reflectionModeOverride":0,"anchorPoint":[223.91257119347284,316.51453168318102],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.09914577717603,316.28247388344346],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.19257120773227,317.01453168185151],"reflectionModeOverride":0,"anchorPoint":[223.19257120773227,317.01453168185151],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.48085346525613,316.9312160768892],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.99257120475204,317.08453168214953],"reflectionModeOverride":0,"anchorPoint":[222.99257120475204,317.08453168214953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.99257120475204,317.08453168214953],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.0925712285939,318.97453166784442],"reflectionModeOverride":0,"anchorPoint":[224.0925712285939,318.97453166784442],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.0925712285939,318.97453166784442],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":8,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.88165496610824,312.75214941635204],"reflectionModeOverride":0,"anchorPoint":[222.39257110465107,312.75452767257332],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257110465107,312.75452767257332],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.45378029794426,313.23854645592189],"reflectionModeOverride":0,"anchorPoint":[220.90257124445034,313.04452773196982],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.37529974656684,312.85070177480884],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.35876943156495,314.2208600575953],"reflectionModeOverride":0,"anchorPoint":[219.70257152159786,313.86452769906913],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.04640821090715,313.51691728007791],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.51848057480817,316.05954140545026],"reflectionModeOverride":0,"anchorPoint":[218.89257170245423,315.09452765536599],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.0841152815558,314.63792794299417],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[219.07885049250976,318.55391401613247],"reflectionModeOverride":0,"anchorPoint":[218.89257166857527,318.09452771720584],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.51848023468915,317.12951359608263],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.04640881460426,319.67213836278347],"reflectionModeOverride":0,"anchorPoint":[219.70257171508007,319.32452783266035],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.35412998721458,318.97193143333453],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.32887552639454,320.75534631866788],"reflectionModeOverride":0,"anchorPoint":[220.90257185775954,320.14452762388811],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[220.45378073087409,319.9505090873252],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.42747093525361,318.97720769048311],"reflectionModeOverride":0,"anchorPoint":[225.07257197531021,319.32452740981233],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.98375014972524,320.42992729161517],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25666363710593,317.12951355179308],"reflectionModeOverride":0,"anchorPoint":[225.8825720147413,318.09452742555698],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.70369149262439,318.55776181404121],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.69870344883279,314.63387871118448],"reflectionModeOverride":0,"anchorPoint":[225.88257204862026,315.09452736371713],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.25666342066654,316.05954129932087],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.73054756988128,313.51475021332715],"reflectionModeOverride":0,"anchorPoint":[225.07257201185996,313.86452742080394],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.42312528062655,314.21540813724465],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[223.40302999066401,312.851739523118],"reflectionModeOverride":0,"anchorPoint":[223.87257194075443,313.04452740721484],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[224.32272320141846,313.23607014436448],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.39257194847221,312.754527354262],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,312.754527354262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.90014945704277,312.7532021443493],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":9,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[221.79793545229796,321.06845412941806],"reflectionModeOverride":0,"anchorPoint":[222.39257194847221,321.06452777387869],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[222.39257194847221,321.06452777387869],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[220.13630955687603,320.47368746402753],"reflectionModeOverride":0,"anchorPoint":[220.66257205810552,320.71452796827054],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.20891937561655,320.94928924957026],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.84008207185138,319.31458146286332],"reflectionModeOverride":0,"anchorPoint":[219.26257159607349,319.71452784560978],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.66107353858615,320.13423302542299],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[217.79637500761001,317.20177600727493],"reflectionModeOverride":0,"anchorPoint":[218.26257166453621,318.30452834140976],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[218.50031051375157,318.83550383670706],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[218.71657507260346,313.79480682341318],"reflectionModeOverride":0,"anchorPoint":[218.26257150938892,314.85452883246262],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[217.79637484257967,315.95728089190948],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[221.15964890699888,312.25319776201661],"reflectionModeOverride":0,"anchorPoint":[220.61257125382446,312.48452913388269],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[219.5567350056233,312.94749659064405],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.93948006388655,312.14028017121217],"reflectionModeOverride":0,"anchorPoint":[222.34257146546875,312.14452894709979],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[221.74863705025209,312.1374427227563],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.61118094730551,312.72639164873863],"reflectionModeOverride":0,"anchorPoint":[224.08257153232208,312.48452915920495],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.53116831317226,312.25589748063879],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[225.90916737355408,313.88955714821628],"reflectionModeOverride":0,"anchorPoint":[225.49257153005306,313.48452928232075],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.08951592668194,313.06563634089633],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.95876793869749,315.99728178436908],"reflectionModeOverride":0,"anchorPoint":[226.49257128177129,314.89452945023424],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.24808810980261,314.36743571685838],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[226.25070868333646,318.87313890226199],"reflectionModeOverride":0,"anchorPoint":[226.49257117287002,318.34452948727852],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[226.95876813673391,317.24177716378313],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[224.65955409863321,320.59420064219239],"reflectionModeOverride":0,"anchorPoint":[225.49257104975428,319.75452948500953],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[225.91146402567489,319.35147381264608],"nextPoint":[112.85257138121662,191.97453151588078]},{"outPoint":[222.34257072457945,321.06452950412722],"reflectionModeOverride":0,"anchorPoint":[222.34257072457945,321.06452950412722],"cornerRadius":0,"prevPoint":[112.85257138121662,191.97453151588078],"inPoint":[223.52534831488276,321.06588609892162],"nextPoint":[112.85257138121662,191.97453151588078]}],"closed":false,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":10,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"}]}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,191.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":6,"smootheningRate":0,"initialPoint":[112.85257138121662,191.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[292.2196979967656,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[270.70254252087443,481.42866614307741],"cornerRadius":0,"prevPoint":[0,1.3634276556741725],"inPoint":[246.45876716860445,474.78563603599895],"nextPoint":[0,1.3634276556741725]},{"outPoint":[308.32019835237998,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.80961189523731,532.13830093176216],"cornerRadius":0,"prevPoint":[9.9345758586960642,16.47175369678871],"inPoint":[307.29902543809465,503.80404406807986],"nextPoint":[9.9345758586960642,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[9.9345758586960642,15.108326041114537],"opacity":1,"blur":0,"isLocked":false,"gid":44,"smootheningRate":0,"initialPoint":[9.9345758586960642,15.108326041114537],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[271.39702488697174,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[345.33038784778722,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[345.33038784778722,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[345.33038784778722,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,480.48121812716067],"reflectionModeOverride":0,"anchorPoint":[271.39702488697174,480.48121812716067],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[271.39702488697174,480.48121812716067],"nextPoint":[86.563617484933189,-170.37985441312253]},{"outPoint":[308.36370636737951,547.81167390718997],"reflectionModeOverride":0,"anchorPoint":[308.36370636737951,547.81167390718997],"cornerRadius":0,"prevPoint":[86.563617484933189,-170.37985441312253],"inPoint":[308.36370636737951,547.81167390718997],"nextPoint":[86.563617484933189,-170.37985441312253]}],"closed":true,"reversed":true}},"fillColor":{"b":0,"s":0,"h":0,"a":1},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[579.21777967228218,-165.50118646031109],"opacity":1,"blur":0,"isLocked":false,"gid":37,"smootheningRate":0,"initialPoint":[579.21777967228218,-165.50118646031109],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[307.98765662154068,497.22766759942954],"reflectionModeOverride":0,"anchorPoint":[307.98765662154068,526.40806463936553],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[307.98765662154068,555.58846167930153],"nextPoint":[0,0]},{"outPoint":[347.07124677007465,468.5633046399771],"reflectionModeOverride":0,"anchorPoint":[331.56894097295356,478.9361833057668],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[316.06663517583246,489.3090619715565],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":33,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"compoundPathData":{"subpaths":[{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[367.7690644381571,667.02363476289884],"reflectionModeOverride":0,"anchorPoint":[367.7690644381571,667.02363476289884],"cornerRadius":0,"prevPoint":[72.193980608055426,108.16792868331186],"inPoint":[368.26333755475412,638.68961321030247],"nextPoint":[72.193980608055426,108.16792868331186]},{"outPoint":[367.82059343747073,616.38299074283532],"reflectionModeOverride":0,"anchorPoint":[367.82059343747073,616.38299074283532],"cornerRadius":0,"prevPoint":[55.02340366515682,81.837967291336099],"inPoint":[367.82059343747073,616.38299074283532],"nextPoint":[55.02340366515682,81.837967291336099]},{"outPoint":[383.13716259159514,626.44746079947163],"reflectionModeOverride":0,"anchorPoint":[403.6767241609266,616.33647011725679],"cornerRadius":0,"prevPoint":[70.109671854888575,134.1591558035534],"inPoint":[403.6767241609266,616.33647011725679],"nextPoint":[70.109671854888575,134.1591558035534]}],"closed":true,"reversed":false}},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[72.357622652088821,121.79167358010636],"opacity":1,"blur":0,"isLocked":false,"gid":48,"smootheningRate":0,"initialPoint":[72.357622652088821,121.79167358010636],"creationPoints":[],"name":"(curve)"}]}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[-2.4740749822735211,26.734403557988003],"opacity":1,"blur":0,"isLocked":false,"gid":46,"smootheningRate":0,"initialPoint":[-2.4740749822735211,26.734403557988003],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"fillColor":{"b":1,"s":0,"h":0,"a":1},"maskedElements":[],"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[189.49257363188801,253.97453151588081],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,253.97453151588081],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,253.97453151588081],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[198.97257317412434,244.48453174476265],"reflectionModeOverride":0,"anchorPoint":[198.97257317412434,244.48453174476265],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[198.97257317412434,244.48453174476265],"nextPoint":[112.85257138121662,152.97453151588078]},{"outPoint":[189.49257363188801,235.01453147773384],"reflectionModeOverride":0,"anchorPoint":[189.49257363188801,235.01453147773384],"cornerRadius":0,"prevPoint":[112.85257138121662,152.97453151588078],"inPoint":[189.49257363188801,235.01453147773384],"nextPoint":[112.85257138121662,152.97453151588078]}],"closed":true,"reversed":false}}},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[112.85257138121662,152.97453151588078],"opacity":1,"blur":0,"isLocked":false,"gid":16,"smootheningRate":0,"initialPoint":[112.85257138121662,152.97453151588078],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[323.38754763944246,487.32457590003901],"reflectionModeOverride":0,"anchorPoint":[344.90470311533363,481.42866614307741],"cornerRadius":0,"prevPoint":[615.60724563620806,1.3634276556741725],"inPoint":[369.14847846760358,474.78563603599895],"nextPoint":[615.60724563620806,1.3634276556741725]},{"outPoint":[307.28704728382809,560.47255779544446],"reflectionModeOverride":0,"anchorPoint":[307.79763374097075,532.13830093176216],"cornerRadius":0,"prevPoint":[605.67266977751206,16.47175369678871],"inPoint":[308.30822019811342,503.80404406807986],"nextPoint":[605.67266977751206,16.47175369678871]}],"closed":false,"reversed":false}},"fillColor":{"b":0.9882352941176471,"s":1,"h":0.13822251558303833,"a":1},"strokeStyle":{"color":{"b":0.9882352941176471,"s":1,"h":0.5806878306878307,"a":1},"dashPattern":[],"join":1,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":0},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[605.67266977751206,16.47175369678871],"opacity":1,"blur":0,"isLocked":false,"gid":45,"smootheningRate":0,"initialPoint":[605.67266977751206,16.47175369678871],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(line)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[13.974435927889729,160.80268978822852],"reflectionModeOverride":0,"anchorPoint":[13.974435927889729,160.80268978822852],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[13.974435927889729,160.80268978822852],"nextPoint":[0,0]},{"outPoint":[144.25556161242727,291.78142890790133],"reflectionModeOverride":0,"anchorPoint":[144.25556161242727,291.78142890790133],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[144.25556161242727,291.78142890790133],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":1,"width":20,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":28,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(line)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[320,664.0506297595],"reflectionModeOverride":0,"anchorPoint":[320,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[320,580],"nextPoint":[0,0]},{"outPoint":[344.050612449646,595.89893977911322],"reflectionModeOverride":0,"anchorPoint":[337.34381007033926,612.48211711168926],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[327.23039949003834,637.48844227310212],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[360,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[352.025306224823,580],"nextPoint":[0,0]},{"outPoint":[260,580],"reflectionModeOverride":0,"anchorPoint":[280,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":true,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":34,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"},{"elementDescription":"(curve)","category":0,"blendMode":0,"creationViewScale":0,"isFreeHandCurve":false,"styleable":{"abstractPath":{"fillRule":0,"pathData":{"nodes":[{"outPoint":[300,640],"reflectionModeOverride":0,"anchorPoint":[300,640],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,640],"nextPoint":[0,0]},{"outPoint":[380,580],"reflectionModeOverride":0,"anchorPoint":[340,580],"cornerRadius":0,"prevPoint":[0,0],"inPoint":[300,580],"nextPoint":[0,0]}],"closed":false,"reversed":false}},"strokeStyle":{"color":{"b":0,"s":0,"h":0,"a":1},"dashPattern":[],"join":2,"width":0.10000000149011612,"endArrow":"","startArrow":"","cap":1},"maskedElements":[]},"angle":0,"smootheningRateRaw":0,"isHidden":false,"endPointFIX":[0,0],"opacity":1,"blur":0,"isLocked":false,"gid":36,"smootheningRate":0,"initialPoint":[0,0],"creationPoints":[],"name":"(curve)"}],"cacheLayers":[],"cacheArtboardPaths":[],"undoStack":[[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11,6]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -29.209948435277369, -232.41660739687057]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14,15]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":12},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":6},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[16]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.01450315572447696,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93007444840156717,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93451308072143913,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93910701763028559,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94055809677531532,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.014503091068591101,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94999337125602745,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95297621548706413,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.013467045153601698,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95790068402842177,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.011044777045815676,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96266052064980523,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0088583736096398309,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96911575815444173,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0077646546444650423,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97217928832645584,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0032726223185911016,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98016728703982747,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0010851843882415254,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98662352066719217,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99307006473711057,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":16}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.20437110010339629, -0, -0, 0.20437110010339629, 126.38769661378409, 170.79330548171168]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[13]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[12,13,14]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":11},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":13},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11}],"methodSignature":"setSuperpath:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":14},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":14}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":1}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":2,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":186.05023255601679,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":558.85414169234264,\"OutPoint_X\":186.05023255601679,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":558.85414169234264},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":185.15633048630471,\"AnchorPoint_Y\":354.17149931944061,\"inPoint_Y\":354.17149931944061,\"OutPoint_X\":185.15633048630471,\"AnchorPoint_X\":185.15633048630471,\"OutPoint_Y\":354.17149931944061},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":386.50447364412418,\"AnchorPoint_Y\":554.43203524141848,\"inPoint_Y\":554.43203524141848,\"OutPoint_X\":386.50447364412418,\"AnchorPoint_X\":386.50447364412418,\"OutPoint_Y\":554.43203524141848},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":284.63851065196616,\"AnchorPoint_Y\":654.20422348213651,\"inPoint_Y\":654.20422348213651,\"OutPoint_X\":284.63851065196616,\"AnchorPoint_X\":284.63851065196616,\"OutPoint_Y\":654.20422348213651},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":286.57710140084737,\"AnchorPoint_Y\":264.05198470231699,\"inPoint_Y\":264.05198470231699,\"OutPoint_X\":286.57710140084737,\"AnchorPoint_X\":286.57710140084737,\"OutPoint_Y\":264.05198470231699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":383.69928863669242,\"AnchorPoint_Y\":361.21410026752545,\"inPoint_Y\":361.21410026752545,\"OutPoint_X\":383.69928863669242,\"AnchorPoint_X\":383.69928863669242,\"OutPoint_Y\":361.21410026752545},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":1,\"NodePoints\":{\"inPoint_X\":235.46249740746129,\"AnchorPoint_Y\":558.85414169234264,\"inPoint_Y\":509.44413050490067,\"OutPoint_X\":136.6379677045723,\"AnchorPoint_X\":186.05023255601679,\"OutPoint_Y\":608.26415287978466},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[11]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":16},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.03408561318607653,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.029926881951800856,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.016189711030229925,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.021585238181938561,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.012961911164688478,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.011895648503707629,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.007872629590133573,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0.005957393322960804,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":37.148059844970703}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":21.344829559326172}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-4.3711390001862419e-08, -0.99999999999999922, 0.99999999999999922, -4.3711390001862419e-08, -173.29768953296772, 744.95852622656867]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 8.2190355245806472, 176.90598115888827]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":11},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.55538688884063114, -0, 182.49084631957811]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 150.32620239929324, 36.081797695525552]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12249755187863798, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.64978701032960984, -0, -0, 0.64978701032960984, 52.531948450558538, 142.05406077476505]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.171276915352394, 196.69205141995428]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.65616231221472443, -0, -0, 0.65616231221472443, 51.425334356867019, 141.12771095949628]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -55.53221494113933, 129.60740108742715]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.17127691535239, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 205.09503537570964, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 208.92985571850454]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[24]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 130.84123782858848]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 75]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 4.5418914390142788, -306.56258907512347]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.96223302635797447, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0392493219135692, -0, -0, 1.0392493219135692, 0, -2.9436991435176969]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.96223302635797447, -0, 2.8325230231519103]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[22]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 15.458108560985721, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 26.562589075123469]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 50]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -25]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[25]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":22}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 9.881426012744555, 0.91199734232554874]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -77.745954011856824, 28.270796276850916]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 67.864527999112269, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.1538461432654479, -0, -0, 1, -1.5384614326544779, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[26]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -136.98574299882625, 7.8612196478959504]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":25},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":24},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":26},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":22},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -53.141264363887032, -43.604121250556744]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -70.052055171480333, -2.5049969673447094]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 219.77475934149726, 163.57319520938375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.6846814845770105, -0, -0, 0.6846814845770105, -0.080018108436871196, 3.1890182924268196]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -85.587957051898314, -21.453286888159028]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDShadowOffsetKey\":10,\"WDShadowAngleKey\":1.5707999467849731,\"WDShadowRadiusKey\":10,\"WDShadowColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":0.33300000000000002,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"$class\":\"WDShadow\"}}","argumentGID":0}],"methodSignature":"setShadow:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":21}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":28},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":19.067760461856238,\"AnchorPoint_Y\":162.4737431408027,\"inPoint_Y\":162.4737431408027,\"OutPoint_X\":19.067760461856238,\"AnchorPoint_X\":19.067760461856238,\"OutPoint_Y\":162.4737431408027},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":142.86303913891427,\"AnchorPoint_Y\":297.21030751597522,\"inPoint_Y\":297.21030751597522,\"OutPoint_X\":142.86303913891427,\"AnchorPoint_X\":142.86303913891427,\"OutPoint_Y\":297.21030751597522},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -258.8309537310077, 14.790477788784642]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":1}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[0.98660023945785613, -0, -0, 1.0127745463822073, 7.5897658853896965, -3.7483052243093384]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[30]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"exchangeObjectAtIndex:withObjectAtIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":27.568212509155273}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":31}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":29}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782582120682,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.98823529481887817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11623311042785645,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11518210172653198,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11062517762184143,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10827497392892838,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10599262267351151,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10487814247608185,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10140901058912277,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10024447739124298,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0991511270403862,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.095819912850856781,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.093592062592506436,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.091305255889892578,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.086611531674861908,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084330290555953952,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.082062393426895142,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.080980166792869596,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.078774556517601013,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.079864569008350372,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.084498241543769864,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.090882599353790255,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.09557187557220459,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.0990910604596138,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10261469334363937,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10601820796728134,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.10719386488199234,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.11685270816087723,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12441384047269821,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.12794080376625061,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13263343274593353,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13615038990974426,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13955500721931458,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14536766707897186,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15242382884025574,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15580509603023529,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15815529227256775,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.16035088896751404,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15694738924503326,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.15200117230415344,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14730300009250641,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14378604292869568,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14143472909927368,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.14034359157085419,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":26}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":30}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[32]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[27,21,30,32,29,31]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[21]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":307.98765662154068,\"AnchorPoint_Y\":526.40806463936553,\"inPoint_Y\":555.58846167930153,\"OutPoint_X\":307.98765662154068,\"AnchorPoint_X\":307.98765662154068,\"OutPoint_Y\":497.22766759942954},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":1,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":20}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":33}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":33},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":9223372036854775807}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":680,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":600},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":320,\"AnchorPoint_Y\":640,\"inPoint_Y\":580,\"OutPoint_X\":320,\"AnchorPoint_X\":320,\"OutPoint_Y\":680.00002878904479},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":380,\"AnchorPoint_X\":360,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":34}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":34},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":660,\"inPoint_Y\":620,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":700},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":35}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":35},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":36}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 20, 20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 640, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 39, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":220,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":300,\"AnchorPoint_X\":300,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{600, 0}\",\"VNPrevPointForRadiusKey\":\"{600, 0}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":260,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":260,\"AnchorPoint_X\":260,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":340,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":340,\"AnchorPoint_X\":340,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 60, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{540, 0}\",\"VNPrevPointForRadiusKey\":\"{540, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":300,\"AnchorPoint_X\":280,\"OutPoint_Y\":600},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-60, 0}\",\"VNPrevPointForRadiusKey\":\"{-60, 0}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[],"methodSignature":"reversePathDirection","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":300,\"AnchorPoint_Y\":580,\"inPoint_Y\":600,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":200,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":280,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":280,\"AnchorPoint_X\":280,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":200,\"AnchorPoint_Y\":580,\"inPoint_Y\":580,\"OutPoint_X\":240,\"AnchorPoint_X\":200,\"OutPoint_Y\":580},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":240,\"AnchorPoint_Y\":640,\"inPoint_Y\":640,\"OutPoint_X\":240,\"AnchorPoint_X\":240,\"OutPoint_Y\":640},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":37},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":3,"argumentEncodedJsonString":"{\"Argument\":false}","argumentGID":0}],"methodSignature":"setClosed:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":36},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -60, 100]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -8.3360061810856791, -0.18596575427056905]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":37}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.12624915594960839, -0.84513498021431133]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0820554726071352, -0, -0, 1, -28.572516179113212, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 3.7693360211719664, 0.58007166688469169]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 0.8911271920692454, -0, 58.840421083655528]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0.018101184397323777, -6.1834757589472247]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0.5616683932951787, -14.721591768371582]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.4335340527209723, 13.544422687729025]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[37]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":37},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":43}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.10440918421369,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":598.11903819000008},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":366.0442444460744,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.21462016330969},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":337.32713106884489,\"AnchorPoint_Y\":505.14299981511687,\"inPoint_Y\":505.14299981511687,\"OutPoint_X\":337.32713106884489,\"AnchorPoint_X\":337.32713106884489,\"OutPoint_Y\":505.14299981511687},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":314.21352490073997,\"AnchorPoint_Y\":480.08137659705397,\"inPoint_Y\":479.94813303079826,\"OutPoint_X\":340.12888467340719,\"AnchorPoint_X\":340.12888467340719,\"OutPoint_Y\":480.08137659705397},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"VNPrevPointForRadiusKey\":\"{-68.909505997160466, -26.2813976538676}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":282.38947067174871,\"AnchorPoint_Y\":502.68576551434728,\"inPoint_Y\":502.68576551434728,\"OutPoint_X\":282.38947067174871,\"AnchorPoint_X\":282.38947067174871,\"OutPoint_Y\":502.68576551434728},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"VNPrevPointForRadiusKey\":\"{51.416878416786517, -23.544099270380286}\",\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":333.80634908853523,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":479.14166624396699,\"OutPoint_X\":333.80634908853523,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":479.14166624396699},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":324.620042723827,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":463.4775893776004},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":342.99265545324346,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":494.80574311033359,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.38209900930354,\"AnchorPoint_Y\":478.82499281784192,\"inPoint_Y\":478.82499281784192,\"OutPoint_X\":268.38209900930354,\"AnchorPoint_X\":268.38209900930354,\"OutPoint_Y\":478.82499281784192},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.22129242932607,\"AnchorPoint_Y\":541.51791009776923,\"inPoint_Y\":484.91678200553838,\"OutPoint_X\":305.16285080676988,\"AnchorPoint_X\":305.16285080676988,\"OutPoint_Y\":541.51791009776923},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":3,\"NodePoints\":{\"inPoint_X\":303.99512941310888,\"AnchorPoint_Y\":479.14166624396699,\"inPoint_Y\":487.18557523329599,\"OutPoint_X\":333.79302393751607,\"AnchorPoint_X\":333.80634908853523,\"OutPoint_Y\":478.98295741265042},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":268.41762507168443,\"AnchorPoint_Y\":478.86160216124927,\"inPoint_Y\":478.86160216124927,\"OutPoint_X\":268.41762507168443,\"AnchorPoint_X\":268.41762507168443,\"OutPoint_Y\":478.86160216124927},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":43}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":249.48159496463126,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":466.59075022213392,\"OutPoint_X\":291.92349007711761,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":493.53972675267255},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":297.36444957939858,\"AnchorPoint_Y\":515.66654723497345,\"inPoint_Y\":487.33229037129115,\"OutPoint_X\":298.38562249368391,\"AnchorPoint_X\":297.87503603654125,\"OutPoint_Y\":544.00080409865575},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":45}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 618.5121544161118, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -17.095091220096265, 18.636572344325828]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[45]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":246.45876716860445,\"AnchorPoint_Y\":480.06523848740324,\"inPoint_Y\":473.42220838032478,\"OutPoint_X\":292.2196979967656,\"AnchorPoint_X\":270.70254252087443,\"OutPoint_Y\":485.96114824436484},\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.29902543809465,\"AnchorPoint_Y\":530.77487327608799,\"inPoint_Y\":502.44061641240569,\"OutPoint_X\":308.32019835237998,\"AnchorPoint_X\":307.80961189523731,\"OutPoint_Y\":559.10913013977029},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"VNPrevPointForRadiusKey\":\"{9.9345758586960642, 15.108326041114537}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":44}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":45},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":44},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 2.4740749822735211, -26.734403557988003]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[46]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":50}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":50}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"VNPrevPointForRadiusKey\":\"{-2.4740749822735211, 26.734403557988003}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":305.84902480507895,\"AnchorPoint_Y\":558.859403557988,\"inPoint_Y\":530.52519454298988,\"OutPoint_X\":305.33842501772648,\"AnchorPoint_X\":305.33842501772648,\"OutPoint_Y\":558.859403557988},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":323.12940602919167,\"AnchorPoint_Y\":534.54856008655031,\"inPoint_Y\":534.54856008655031,\"OutPoint_X\":323.12940602919167,\"AnchorPoint_X\":323.12940602919167,\"OutPoint_Y\":534.54856008655031},\"$class\":\"WDBezierNode\"},{\"NodeTypeOverride\":0,\"NodePoints\":{\"inPoint_X\":342.43217501772648,\"AnchorPoint_Y\":508.171903557988,\"inPoint_Y\":508.171903557988,\"OutPoint_X\":320.91501841129599,\"AnchorPoint_X\":342.43217501772648,\"OutPoint_Y\":514.06781336097845},\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[47,48]}","argumentGID":0}],"methodSignature":"setSubpaths:","targetGID":46},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46}],"methodSignature":"setSuperpath:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setFill:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setShadow:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":46},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.13822251558303833,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -2.1531569701203352, 25.991399082341331]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":308.00218177519929,\"AnchorPoint_Y\":532.86800447564667,\"inPoint_Y\":504.53379546064855,\"OutPoint_X\":307.49158198784681,\"AnchorPoint_X\":307.49158198784681,\"OutPoint_Y\":532.86800447564667},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.54481307650724,\"AnchorPoint_Y\":482.22702540901258,\"inPoint_Y\":482.22702540901258,\"OutPoint_X\":307.54481307650724,\"AnchorPoint_X\":307.54481307650724,\"OutPoint_Y\":482.22702540901258},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"VNPrevPointForRadiusKey\":\"{-15.584592952684432, -52.321534677537727}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":344.58533198784681,\"AnchorPoint_Y\":482.18050447564667,\"inPoint_Y\":482.18050447564667,\"OutPoint_X\":323.06817538141632,\"AnchorPoint_X\":344.58533198784681,\"OutPoint_Y\":488.07641427863712},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"VNPrevPointForRadiusKey\":\"{2.1531569701203352, -25.991399082341331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 0, -1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.34003453224391933]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 0.1954377384541317]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[48]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -20, -20]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[-1, -0, 0, 1, 692.07691397569363, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"KeepNodesAfterTransformTransformOptionKey\":true}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 57.024957634971202, 19.792602829855639]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[54]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":307.04977456552314,\"AnchorPoint_Y\":532.53992937509292,\"inPoint_Y\":504.20572036009486,\"OutPoint_X\":307.56037435287561,\"AnchorPoint_X\":307.56037435287561,\"OutPoint_Y\":532.53992937509292},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"VNPrevPointForRadiusKey\":\"{612.89879937060209, -26.319474182895021}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":307.50714326421519,\"AnchorPoint_Y\":481.89895030845889,\"inPoint_Y\":481.89895030845889,\"OutPoint_X\":307.50714326421519,\"AnchorPoint_X\":307.50714326421519,\"OutPoint_Y\":481.89895030845889},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"VNPrevPointForRadiusKey\":\"{630.6365492934068, -52.649609778091417}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":270.46662435287561,\"AnchorPoint_Y\":481.85242937509298,\"inPoint_Y\":481.85242937509298,\"OutPoint_X\":291.68464211968808,\"AnchorPoint_X\":270.46662435287561,\"OutPoint_Y\":491.96348695323337},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"VNPrevPointForRadiusKey\":\"{615.05195634072243, -0.32807510055368994}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":54},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":8}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":48},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":6}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":55},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0330248439984473, -0, -0, 0.99999999999999989, -11.379876833051835, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[55]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":55},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":7}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":21},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":30},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":2}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":32},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":3}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":27},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":29},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":31},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setElements:","targetGID":56},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":27},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":0}],"methodSignature":"setGroup:","targetGID":55}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":57}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -290.115478515625, 62.302734375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.89467432944640213,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.042222879700741525,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.90097236067321962,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.031593452065677957,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.91739226661025597,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.019517801575741518,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.93616537665754818,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0091232041181144082,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.94328004740819738,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0.0018620895127118644,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95006183205443251,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.95531556443573817,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9587083584477003,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96669617604784863,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.96977202191904677,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97283138648692502,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.97731792325435829,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9818318081185089,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9863455118694362,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99094089768406901,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.99553628349870182,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"$class\":\"StrokeStyle\",\"WDColorKey\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":0,\"$class\":\"Vectornator.Color\"},\"WDEndArrowKey\":\"\",\"WDCapKey\":0,\"WDStartArrowKey\":\"\",\"WDJoinKey\":1,\"WDWeightKey\":0.10000000149011612}}","argumentGID":0}],"methodSignature":"setStrokeStyle:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -138.240478515625, -358.288818359375]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56},{"argumentTypeRawValue":1,"argumentEncodedJsonString":"{\"Argument\":0}","argumentGID":0}],"methodSignature":"insertObject:atIndex:","targetGID":4},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentGID":56}],"methodSignature":"removeObject:","targetGID":4}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 10.2197265625, 7.887451171875]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1.73681640625, -1.3956298828125]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":0,\"WDHueKey\":0,\"WDAlphaKey\":1,\"WDBrightnessKey\":1,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.5806878306878307,\"WDAlphaKey\":1,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96954748896640086,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.96362693977790437,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.95841712343394081,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.954131593465262,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.93934857275056949,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.83357840083997725,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.80253639308086555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.76741239856207288,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.7565612097807517,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.73508462058656032,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.69529915290432798,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.68285743522209563,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.66214385499715267,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.64970324957289294,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.63305608449601369,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.62061547907175396,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.60169597095671978,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.59555519468963558,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5870419721668565,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.57588046341116172,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.56733832218109337,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.5457271497722096,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.51220146996013671,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.45939702270785876,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.41278562784738043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.38011749893223234,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.35006206399487472,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.33336373505125283,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31040673049544421,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29577720138097952,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.27499243664578588,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.25639993237471526,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24782776017938496,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23669294561503418,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22722206897779043,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.22344372864464693,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21991675861332574,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.21874110193621868,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2197966347522779,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.23113499252562641,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.24180488325740318,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.2529452591116173,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.28146132901480636,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29042946504840544,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.29303437322038722,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30319262528473806,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31050016016514809,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31284924900341687,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31402379342255127,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31192273811218679,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.31087054207004555,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.30975717183940776,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"WDSaturationKey\":1,\"WDHueKey\":0.58068782091140747,\"WDAlphaKey\":0.3074892778331435,\"WDBrightnessKey\":0.9882352941176471,\"$class\":\"Vectornator.Color\"}}","argumentGID":0}],"methodSignature":"setFill:","targetGID":57}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1.0000066161593433, -0, -0, 1.0000066161593433, -0.00084115369224984499, -0.0015278888873493538]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -0, 1]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 106.25150911801597, 215.10157288799655]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":1,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1024, 1024}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1018.1834573966, 1022.316502396953}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1008.5644489681886, 1015.9218488910001}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {1003.3383988868811, 1012.188740268123}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {959.56182404754463, 968.37466824766148}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {930.99873639690077, 938.04865943448704}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {883.88375480634159, 898.86742705675647}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {851.39277451811745, 876.13749149913815}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {807.42735481491377, 847.80421878677203}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {780.8131139470363, 833.66058405713125}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {758.18676120653936, 823.6793880913026}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {745.42543866806363, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.45307691385142, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.26800024929958}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {738.07015268029545, 820.65313908147255}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {735.7456294412641, 820.1359296408807}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {734.20024226104533, 819.105537302637}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {730.82720724054207, 815.73250228213374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {722.51078545309133, 807.41391622785272}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {715.22999050970566, 800.13291995732015}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {710.02528110597814, 794.3195482564854}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {708.99086222479468, 792.76802059828378}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {705.03881032957679, 786.83843280185465}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {703.71729893680003, 785.04591654871797}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {702.86608775936838, 784.19455437592615}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {701.16326275021129, 782.49188036212945}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {700.77188277648224, 782.10024872946656}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.97704320020512, 781.30550981676288}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {699.12865060283116, 780.87985389626033}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {698.70304501411556, 780.45414764397106}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {695.54100084364723, 776.8204946317021}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {693.70127337454369, 774.98056583545167}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {691.86235121402819, 772.67134345959084}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.97672115541332, 769.84279770809485}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {689.03491276184081, 768.90119064166925}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {686.67817717929256, 766.07264489017348}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {683.90469440249581, 763.76603976722345}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {682.53607245732564, 762.86897633206718}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {681.17026909221318, 762.39756881741346}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {678.95083862390538, 761.54620664462163}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {676.30580256688177, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.07408308165805, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.28025820206608, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {671.48603066818009, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.54341696601955, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.17600298373122, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.39103649860635, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.44842279644581, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {666.02281720772999, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.226367014277, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {663.57830298909857, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.7291050831368, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.92701772956843, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.54691207607016, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 761.07479912996791}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.69429082217562}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 760.30406847954168}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.91817467056762}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.52795232793369}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 759.12605301077474}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.73814593033103}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.15190821369538, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 758.35446671997374}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.96107347477482}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.55134127330371, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.97694686201953, 757.57593464260208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 757.18274272455005}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.3896675133285, 756.35493582795493}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.94518475206382}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 755.5517915068649}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.78614436689986}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.3960730196261}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 754.00237778370661}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.60918586565458}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 753.21065877820774}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.81701387407497}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 752.41430924832821}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 749.69714774089903}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 748.93285955917622}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.99004452986878}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 747.13868235707696}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.79916693028599, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.41906127678772, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.65522608114543, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.27149653900142, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.88132452815421, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.48793128295529, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.69429966956, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.30453031300658, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {659.68503862079888, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.0695734715307, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.47263041978476, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {660.86843959074758, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.25498771294906, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {661.63670398362319, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.02566803158879, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {662.83158060094979, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {664.08383545516563, 745.8865784982213}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {665.7862578100287, 746.7378903392264}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {667.06307457617595, 747.16359659151567}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.70851328749336, 748.01490843252077}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.98533005364084, 748.44061468481004}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.66399580516031, 748.84437627807847}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.94081257130756, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.19387273411121, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.58525270784025, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {675.20474440004818, 749.69478214692208}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {673.93235683113426, 749.26907589463281}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3},{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {672.1381292972485, 747.52296554887494}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.87500277709569, 747.09725929658589}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {670.47194582884163, 746.2471050866759}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.64368594616599, 745.41864387685314}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 745.02308636482417}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {669.24787677520317, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":289.25600703483042,\"inPoint_Y\":289.25600703483042,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":289.25600703483042},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477645924727,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":437.28477645924727,\"AnchorPoint_X\":437.28477645924727,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25435579077384,\"inPoint_Y\":539.25435579077384,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":539.25435579077384},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"VNPrevPointForRadiusKey\":\"{59.351577569932488, -881.2368652940595}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":22},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":330.67709661876142,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":247.83495910094825},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":214.25650324349817,\"inPoint_Y\":214.25650324349817,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":214.25650324349817},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":289.25602785985438,\"inPoint_Y\":247.83495910094825,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":330.67709661876142},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":364.25550551101242,\"inPoint_Y\":364.25550551101242,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":364.25550551101242},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -465.50590420624349}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":24},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":287.2857717617394,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":580.67544258986891,\"OutPoint_X\":287.2857717617394,\"AnchorPoint_X\":287.2857717617394,\"OutPoint_Y\":497.83330507205574},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":320.86422761918971,\"AnchorPoint_Y\":464.25484921460566,\"inPoint_Y\":464.25484921460566,\"OutPoint_X\":403.70636513700288,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":464.25484921460566},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":437.28477402925387,\"AnchorPoint_Y\":539.25437383096278,\"inPoint_Y\":497.83330507205574,\"OutPoint_X\":437.28477402925387,\"AnchorPoint_X\":437.28477402925387,\"OutPoint_Y\":580.67544258986891},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":403.70636513700288,\"AnchorPoint_Y\":614.2538514821199,\"inPoint_Y\":614.2538514821199,\"OutPoint_X\":320.86422761918971,\"AnchorPoint_X\":362.28529637809629,\"OutPoint_Y\":614.2538514821199},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"VNPrevPointForRadiusKey\":\"{-173.55249370630804, -215.50755823513555}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":25},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":289.07321462503751,\"inPoint_Y\":289.07321462503751,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":289.07321462503751},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":427.28484419717574,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":427.28484419717574,\"AnchorPoint_X\":427.28484419717574,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":297.2857056005837,\"AnchorPoint_Y\":539.07156338098093,\"inPoint_Y\":539.07156338098093,\"OutPoint_X\":297.2857056005837,\"AnchorPoint_X\":297.2857056005837,\"OutPoint_Y\":539.07156338098093},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"VNPrevPointForRadiusKey\":\"{99.742735489559436, -881.41965770385173}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":26},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":97.57351080287799,\"AnchorPoint_Y\":309.16721333385931,\"inPoint_Y\":309.16721333385931,\"OutPoint_X\":97.57351080287799,\"AnchorPoint_X\":97.57351080287799,\"OutPoint_Y\":309.16721333385931},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":221.36797043606543,\"AnchorPoint_Y\":443.90288627635027,\"inPoint_Y\":443.90288627635027,\"OutPoint_X\":221.36797043606543,\"AnchorPoint_X\":221.36797043606543,\"OutPoint_Y\":443.90288627635027},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":361.08361414401247,\"AnchorPoint_Y\":309.6035637643763,\"inPoint_Y\":309.6035637643763,\"OutPoint_X\":361.08361414401247,\"AnchorPoint_X\":361.08361414401247,\"OutPoint_Y\":309.6035637643763},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":29},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":377.82365759764593,\"AnchorPoint_Y\":307.07623767819223,\"inPoint_Y\":307.07623767819223,\"OutPoint_X\":377.82365759764593,\"AnchorPoint_X\":377.82365759764593,\"OutPoint_Y\":307.07623767819223},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":503.2994629767461,\"AnchorPoint_Y\":440.11243356580462,\"inPoint_Y\":440.11243356580462,\"OutPoint_X\":503.2994629767461,\"AnchorPoint_X\":503.2994629767461,\"OutPoint_Y\":440.11243356580462},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":644.91269001662772,\"AnchorPoint_Y\":307.50708423933065,\"inPoint_Y\":307.50708423933065,\"OutPoint_X\":644.91269001662772,\"AnchorPoint_X\":644.91269001662772,\"OutPoint_Y\":307.50708423933065},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"VNPrevPointForRadiusKey\":\"{358.49705139509047, 146.65290478922907}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":30},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":516.45695350382334,\"AnchorPoint_Y\":154.80813254227905,\"inPoint_Y\":154.80813254227905,\"OutPoint_X\":516.45695350382334,\"AnchorPoint_X\":516.45695350382334,\"OutPoint_Y\":154.80813254227905},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":223.97172601151692,\"AnchorPoint_Y\":448.88181029919406,\"inPoint_Y\":448.88181029919406,\"OutPoint_X\":223.97172601151692,\"AnchorPoint_X\":223.97172601151692,\"OutPoint_Y\":448.88181029919406},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":78.252109034647219,\"AnchorPoint_Y\":300.1041732651538,\"inPoint_Y\":300.1041732651538,\"OutPoint_X\":78.252109034647219,\"AnchorPoint_X\":78.252109034647219,\"OutPoint_Y\":300.1041732651538},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":648.07861404640448,\"AnchorPoint_Y\":302.9355554780409,\"inPoint_Y\":302.9355554780409,\"OutPoint_X\":648.07861404640448,\"AnchorPoint_X\":648.07861404640448,\"OutPoint_Y\":302.9355554780409},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":506.17105679039673,\"AnchorPoint_Y\":444.7847840681311,\"inPoint_Y\":444.7847840681311,\"OutPoint_X\":506.17105679039673,\"AnchorPoint_X\":506.17105679039673,\"OutPoint_Y\":444.7847840681311},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":217.51314851045686,\"AnchorPoint_Y\":156.11368443580113,\"inPoint_Y\":156.11368443580113,\"OutPoint_X\":217.51314851045686,\"AnchorPoint_X\":217.51314851045686,\"OutPoint_Y\":156.11368443580113},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"VNPrevPointForRadiusKey\":\"{1033.732755685123, -115.61701281386331}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":21},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":418.73751961026619,\"inPoint_Y\":418.73751961026619,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":418.73751961026619},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 146.69454513811797}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":31},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":290.26965144695237,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":290.26965144695237,\"AnchorPoint_X\":290.26965144695237,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":433.31216208615911,\"AnchorPoint_Y\":521.73683815036247,\"inPoint_Y\":521.73683815036247,\"OutPoint_X\":433.31216208615911,\"AnchorPoint_X\":433.31216208615911,\"OutPoint_Y\":521.73683815036247},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"VNPrevPointForRadiusKey\":\"{78.505876495528582, 249.69386367821471}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":32},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":362.67570746371234,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.18143434711533,\"AnchorPoint_X\":362.18143434711533,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{66.606350517013652, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.23296334642896,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.23296334642896,\"AnchorPoint_X\":362.23296334642896,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{49.435773574115046, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":398.08909406988482,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":377.54953250055337,\"AnchorPoint_X\":398.08909406988482,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{64.522041763846801, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":48},{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":[{\"NodePoints\":{\"inPoint_X\":361.7537539269963,\"AnchorPoint_Y\":667.02363476289884,\"inPoint_Y\":638.68961321030247,\"OutPoint_X\":362.24802704359331,\"AnchorPoint_X\":362.24802704359331,\"OutPoint_Y\":667.02363476289884},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"VNPrevPointForRadiusKey\":\"{657.82311087369476, 108.16792868331186}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":362.19649804427968,\"AnchorPoint_Y\":616.38299074283532,\"inPoint_Y\":616.38299074283532,\"OutPoint_X\":362.19649804427968,\"AnchorPoint_X\":362.19649804427968,\"OutPoint_Y\":616.38299074283532},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"VNPrevPointForRadiusKey\":\"{674.99368781659336, 81.837967291336099}\",\"$class\":\"WDBezierNode\"},{\"NodePoints\":{\"inPoint_X\":326.34036732082382,\"AnchorPoint_Y\":616.33647011725679,\"inPoint_Y\":616.33647011725679,\"OutPoint_X\":346.87992889015482,\"AnchorPoint_X\":326.34036732082382,\"OutPoint_Y\":626.44746079947163},\"NodeTypeOverride\":0,\"VNNextPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"VNPrevPointForRadiusKey\":\"{659.90741962686184, 134.1591558035534}\",\"$class\":\"WDBezierNode\"}]}","argumentGID":0}],"methodSignature":"setNodes:","targetGID":54}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, -1, 0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"proxy","methodArguments":[{"argumentTypeRawValue":6,"argumentEncodedJsonString":"{\"Argument\":\"[1, -0, -0, 1, 1, -0]\"}","argumentGID":0},{"argumentTypeRawValue":5,"argumentEncodedJsonString":"{\"Argument\":[57,56]}","argumentGID":0},{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"UpdateAngleAfterTransformTransformOptionKey\":0,\"TransformingNodesTransformOptionKey\":false}}","argumentGID":0}],"methodSignature":"applyTransform:forElements:options:","targetGID":0}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":\"Mac App icon\"}","argumentGID":0}],"methodSignature":"title","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {740, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}],[{"actionTypeRawValue":"dynamicProxy","methodArguments":[{"argumentTypeRawValue":4,"argumentEncodedJsonString":"{\"Argument\":{\"NS.special\":3,\"NS.rectval\":\"{{0, 0}, {745, 744.6337699943515}}\",\"$class\":\"NSValue\"}}","argumentGID":0}],"methodSignature":"frame","targetGID":3}]],"isGroupingUndo":false,"currentUndoGroup":[],"currentRedoGroup":[],"isGroupingRedo":false,"redoStack":[],"nextGID":57} diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index 3bade7b..0ddc982 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -7,6 +7,8 @@ nav: - Getting Started: getting_started.md - Development: - Python Environments: development/python_environments.md + - Contributing: development/contributing.md + - Code Style: development/code_style.md - Use Cases: - Overview: use_cases/index.md - Use Case 1: use_cases/use_case_1.md @@ -41,10 +43,15 @@ nav: - Apps & Tools: - Overview: apps_and_tools/index.md - Console: apps_and_tools/console.md - - Link Relay: apps_and_tools/link_relay.md + - Bench: apps_and_tools/bench.md - HCI Bridge: apps_and_tools/hci_bridge.md - Golden Gate Bridge: apps_and_tools/gg_bridge.md - Show: apps_and_tools/show.md + - GATT Dump: apps_and_tools/gatt_dump.md + - Pair: apps_and_tools/pair.md + - Unbond: apps_and_tools/unbond.md + - USB Probe: apps_and_tools/usb_probe.md + - Link Relay: apps_and_tools/link_relay.md - Hardware: - Overview: hardware/index.md - Platforms: @@ -56,7 +63,7 @@ nav: - Examples: - Overview: examples/index.md -copyright: Copyright 2021-2022 Google LLC +copyright: Copyright 2021-2023 Google LLC theme: name: 'material' diff --git a/docs/mkdocs/requirements.txt b/docs/mkdocs/requirements.txt index a9b8452..a241683 100644 --- a/docs/mkdocs/requirements.txt +++ b/docs/mkdocs/requirements.txt @@ -1,6 +1,6 @@ # This requirements file is for python3 -mkdocs == 1.2.3 -mkdocs-material == 7.1.7 -mkdocs-material-extensions == 1.0.1 -pymdown-extensions == 8.2 -mkdocstrings == 0.15.1
\ No newline at end of file +mkdocs == 1.4.0 +mkdocs-material == 8.5.6 +mkdocs-material-extensions == 1.0.3 +pymdown-extensions == 10.0 +mkdocstrings-python == 0.7.1 diff --git a/docs/mkdocs/src/api/examples.md b/docs/mkdocs/src/api/examples.md index bcf30a3..3f73b7b 100644 --- a/docs/mkdocs/src/api/examples.md +++ b/docs/mkdocs/src/api/examples.md @@ -1,2 +1,2 @@ API EXAMPLES -============
\ No newline at end of file +============ diff --git a/docs/mkdocs/src/api/guide.md b/docs/mkdocs/src/api/guide.md index b799faf..2a4aa06 100644 --- a/docs/mkdocs/src/api/guide.md +++ b/docs/mkdocs/src/api/guide.md @@ -1,2 +1,2 @@ API DEVELOPER GUIDE -===================
\ No newline at end of file +=================== diff --git a/docs/mkdocs/src/api/reference.md b/docs/mkdocs/src/api/reference.md index cedc2c1..a5ff1bc 100644 --- a/docs/mkdocs/src/api/reference.md +++ b/docs/mkdocs/src/api/reference.md @@ -16,4 +16,3 @@ Bumble Python API ### HCI_Disconnect_Command ::: bumble.hci.HCI_Disconnect_Command - diff --git a/docs/mkdocs/src/apps_and_tools/bench.md b/docs/mkdocs/src/apps_and_tools/bench.md new file mode 100644 index 0000000..db785d6 --- /dev/null +++ b/docs/mkdocs/src/apps_and_tools/bench.md @@ -0,0 +1,158 @@ +BENCH TOOL +========== + +The "bench" tool implements a number of different ways of measuring the +throughput and/or latency between two devices. + +# General Usage + +``` +Usage: bench.py [OPTIONS] COMMAND [ARGS]... + +Options: + --device-config FILENAME Device configuration file + --role [sender|receiver|ping|pong] + --mode [gatt-client|gatt-server|l2cap-client|l2cap-server|rfcomm-client|rfcomm-server] + --att-mtu MTU GATT MTU (gatt-client mode) [23<=x<=517] + -s, --packet-size SIZE Packet size (server role) [8<=x<=4096] + -c, --packet-count COUNT Packet count (server role) + -sd, --start-delay SECONDS Start delay (server role) + --help Show this message and exit. + +Commands: + central Run as a central (initiates the connection) + peripheral Run as a peripheral (waits for a connection) +``` + +## Options for the ``central`` Command +``` +Usage: bumble-bench central [OPTIONS] TRANSPORT + + Run as a central (initiates the connection) + +Options: + --peripheral ADDRESS_OR_NAME Address or name to connect to + --connection-interval, --ci CONNECTION_INTERVAL + Connection interval (in ms) + --phy [1m|2m|coded] PHY to use + --help Show this message and exit. +``` + + +To test once device against another, one of the two devices must be running +the ``peripheral`` command and the other the ``central`` command. The device +running the ``peripheral`` command will accept connections from the device +running the ``central`` command. +When using Bluetooth LE (all modes except for ``rfcomm-server`` and ``rfcomm-client``utils), +the default addresses configured in the tool should be sufficient. But when using +Bluetooth Classic, the address of the Peripheral must be specified on the Central +using the ``--peripheral`` option. The address will be printed by the Peripheral when +it starts. + +Independently of whether the device is the Central or Peripheral, each device selects a +``mode`` and and ``role`` to run as. The ``mode`` and ``role`` of the Central and Peripheral +must be compatible. + +Device 1 mode | Device 2 mode +------------------|------------------ +``gatt-client`` | ``gatt-server`` +``l2cap-client`` | ``l2cap-server`` +``rfcomm-client`` | ``rfcomm-server`` + +Device 1 role | Device 2 role +--------------|-------------- +``sender`` | ``receiver`` +``ping`` | ``pong`` + + +# Examples + +In the following examples, we have two USB Bluetooth controllers, one on `usb:0` and +the other on `usb:1`, and two consoles/terminals. We will run a command in each. + +!!! example "GATT Throughput" + Using the default mode and role for the Central and Peripheral. + + In the first console/terminal: + ``` + $ bumble-bench peripheral usb:0 + ``` + + In the second console/terminal: + ``` + $ bumble-bench central usb:1 + ``` + + In this default configuration, the Central runs a Sender, as a GATT client, + connecting to the Peripheral running a Receiver, as a GATT server. + +!!! example "L2CAP Throughput" + In the first console/terminal: + ``` + $ bumble-bench --mode l2cap-server peripheral usb:0 + ``` + + In the second console/terminal: + ``` + $ bumble-bench --mode l2cap-client central usb:1 + ``` + +!!! example "RFComm Throughput" + In the first console/terminal: + ``` + $ bumble-bench --mode rfcomm-server peripheral usb:0 + ``` + + NOTE: the BT address of the Peripheral will be printed out, use it with the + ``--peripheral`` option for the Central. + + In this example, we use a larger packet size and packet count than the default. + + In the second console/terminal: + ``` + $ bumble-bench --mode rfcomm-client --packet-size 2000 --packet-count 100 central --peripheral 00:16:A4:5A:40:F2 usb:1 + ``` + +!!! example "Ping/Pong Latency" + In the first console/terminal: + ``` + $ bumble-bench --role pong peripheral usb:0 + ``` + + In the second console/terminal: + ``` + $ bumble-bench --role ping central usb:1 + ``` + +!!! example "Reversed modes with GATT and custom connection interval" + In the first console/terminal: + ``` + $ bumble-bench --mode gatt-client peripheral usb:0 + ``` + + In the second console/terminal: + ``` + $ bumble-bench --mode gatt-server central --ci 10 usb:1 + ``` + +!!! example "Reversed modes with L2CAP and custom PHY" + In the first console/terminal: + ``` + $ bumble-bench --mode l2cap-client peripheral usb:0 + ``` + + In the second console/terminal: + ``` + $ bumble-bench --mode l2cap-server central --phy 2m usb:1 + ``` + +!!! example "Reversed roles with L2CAP" + In the first console/terminal: + ``` + $ bumble-bench --mode l2cap-client --role sender peripheral usb:0 + ``` + + In the second console/terminal: + ``` + $ bumble-bench --mode l2cap-server --role receiver central usb:1 + ``` diff --git a/docs/mkdocs/src/apps_and_tools/console.md b/docs/mkdocs/src/apps_and_tools/console.md index 9abdf2b..c8e1769 100644 --- a/docs/mkdocs/src/apps_and_tools/console.md +++ b/docs/mkdocs/src/apps_and_tools/console.md @@ -7,10 +7,12 @@ The Console app is an interactive text user interface that offers a number of fu * scanning * advertising - * connecting to devices + * connecting to and disconnecting from devices * changing connection parameters + * enabling encryption * discovering GATT services and characteristics - * read & write GATT characteristics + * reading and writing GATT characteristics + * subscribing to and unsubscribing from GATT characteristics The console user interface has 3 main panes: diff --git a/docs/mkdocs/src/apps_and_tools/gg_bridge.md b/docs/mkdocs/src/apps_and_tools/gg_bridge.md index 950f47c..316f7a3 100644 --- a/docs/mkdocs/src/apps_and_tools/gg_bridge.md +++ b/docs/mkdocs/src/apps_and_tools/gg_bridge.md @@ -1,2 +1,2 @@ GOLDEN GATE BRIDGE -==================
\ No newline at end of file +================== diff --git a/docs/mkdocs/src/apps_and_tools/hci_bridge.md b/docs/mkdocs/src/apps_and_tools/hci_bridge.md index c8def63..d0ea1fc 100644 --- a/docs/mkdocs/src/apps_and_tools/hci_bridge.md +++ b/docs/mkdocs/src/apps_and_tools/hci_bridge.md @@ -28,5 +28,3 @@ a host that send custom HCI commands that the controller may not understand. (through which the communication with other virtual controllers will be mediated). NOTE: this assumes you're running a Link Relay on port `10723`. - - diff --git a/docs/mkdocs/src/apps_and_tools/index.md b/docs/mkdocs/src/apps_and_tools/index.md index cd8b346..fe7af56 100644 --- a/docs/mkdocs/src/apps_and_tools/index.md +++ b/docs/mkdocs/src/apps_and_tools/index.md @@ -5,10 +5,10 @@ Included in the project are a few apps and tools, built on top of the core libra These include: * [Console](console.md) - an interactive text-based console + * [Bench](bench.md) - Speed and Latency benchmarking between two devices (LE and Classic) * [Pair](pair.md) - Pair/bond two devices (LE and Classic) * [Unbond](unbond.md) - Remove a previously established bond * [HCI Bridge](hci_bridge.md) - a HCI transport bridge to connect two HCI transports and filter/snoop the HCI packets * [Golden Gate Bridge](gg_bridge.md) - a bridge between GATT and UDP to use with the Golden Gate "stack tool" * [Show](show.md) - Parse a file with HCI packets and print the details of each packet in a human readable form * [Link Relay](link_relay.md) - WebSocket relay for virtual RemoteLink instances to communicate with each other. - diff --git a/docs/mkdocs/src/apps_and_tools/link_relay.md b/docs/mkdocs/src/apps_and_tools/link_relay.md index 533c525..ff3fb8b 100644 --- a/docs/mkdocs/src/apps_and_tools/link_relay.md +++ b/docs/mkdocs/src/apps_and_tools/link_relay.md @@ -31,4 +31,3 @@ The WebSocket path used by a connecting client indicates which virtual "chat roo It is possible to connect to a "chat room" in a relay as an observer, rather than a virtual controller. In this case, a text-based console can be used to observe what is going on in the "chat room". Tools like [`wscat`](https://github.com/websockets/wscat#readme) or [`websocat`](https://github.com/vi/websocat) can be used for that. Example: `wscat --connect ws://localhost:10723/test` - diff --git a/docs/mkdocs/src/apps_and_tools/usb_probe.md b/docs/mkdocs/src/apps_and_tools/usb_probe.md new file mode 100644 index 0000000..dd2585d --- /dev/null +++ b/docs/mkdocs/src/apps_and_tools/usb_probe.md @@ -0,0 +1,50 @@ +USB PROBE TOOL +============== + +This tool lists all the USB devices, with details about each device. +For each device, the different possible Bumble transport strings that can +refer to it are listed. +If the device is known to be a Bluetooth HCI device, its identifier is printed +in reverse colors, and the transport names in cyan color. +For other devices, regardless of their type, the transport names are printed +in red. Whether that device is actually a Bluetooth device or not depends on +whether it is a Bluetooth device that uses a non-standard Class, or some other +type of device (there's no way to tell). + +## Usage + +This command line tool may be invoked with no arguments, or with `--verbose` +for extra details. +When installed from PyPI, run as +``` +$ bumble-usb-probe +``` + +or, for extra details, with the `--verbose` argument +``` +$ bumble-usb-probe --v +``` + +When running from the source distribution: +``` +$ python3 apps/usb-probe.py +``` + +or + +``` +$ python3 apps/usb-probe.py --verbose +``` + +!!! example + ``` + $ python3 apps/usb_probe.py + + ID 0A12:0001 + Bumble Transport Names: usb:0 or usb:0A12:0001 + Bus/Device: 020/034 + Class: Wireless Controller + Subclass/Protocol: 1/1 [Bluetooth] + Manufacturer: None + Product: USB2.0-BT + ``` diff --git a/docs/mkdocs/src/components/controller.md b/docs/mkdocs/src/components/controller.md index 1537712..3588e0f 100644 --- a/docs/mkdocs/src/components/controller.md +++ b/docs/mkdocs/src/components/controller.md @@ -1,2 +1,2 @@ CONTROLLER -==========
\ No newline at end of file +========== diff --git a/docs/mkdocs/src/components/gatt.md b/docs/mkdocs/src/components/gatt.md index 614f03c..cdc06d4 100644 --- a/docs/mkdocs/src/components/gatt.md +++ b/docs/mkdocs/src/components/gatt.md @@ -1,2 +1,2 @@ GATT -====
\ No newline at end of file +==== diff --git a/docs/mkdocs/src/components/host.md b/docs/mkdocs/src/components/host.md index 5a34473..dc2dbe6 100644 --- a/docs/mkdocs/src/components/host.md +++ b/docs/mkdocs/src/components/host.md @@ -1,2 +1,2 @@ HOST -====
\ No newline at end of file +==== diff --git a/docs/mkdocs/src/components/security_manager.md b/docs/mkdocs/src/components/security_manager.md index 4ce3d74..4fa54bf 100644 --- a/docs/mkdocs/src/components/security_manager.md +++ b/docs/mkdocs/src/components/security_manager.md @@ -1,2 +1,2 @@ SECURITY MANAGER -================
\ No newline at end of file +================ diff --git a/docs/mkdocs/src/development/code_style.md b/docs/mkdocs/src/development/code_style.md new file mode 100644 index 0000000..1c2bbb9 --- /dev/null +++ b/docs/mkdocs/src/development/code_style.md @@ -0,0 +1,43 @@ +CODE STYLE +========== + +The Python code style used in this project follows the [Black code style](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html). + +# Formatting + +For now, we are configuring the `black` formatter with the option to leave quotes unchanged. +The preferred quote style is single quotes, which isn't a configurable option for `Black`, so we are not enforcing it. This may change in the future. + +## Ignoring Commit for Git Blame + +The adoption of `Black` as a formatter came in late in the project, with already a large code base. As a result, a large number of files were changed in a single commit, which gets in the way of tracing authorship with `git blame`. The file `git-blame-ignore-revs` contains the commit hash of when that mass-formatting event occurred, which you can use to skip it in a `git blame` analysis: + +!!! example "Ignoring a commit with `git blame`" + ``` + $ git blame --ignore-revs-file .git-blame-ignore-revs + ``` + +# Linting + +The project includes a `pylint` configuration (see the `pyproject.toml` file for details). +The `pre-commit` checks only enforce that there are no errors. But we strongly recommend that you run the linter with warnings enabled at least, and possibly the "Refactor" ('R') and "Convention" ('C') categories as well. +To run the linter, use the `project.lint` invoke command. + +!!! example "Running the linter with default options" + With the default settings, Errors and Warnings are enabled, but Refactor and Convention categories are not. + ``` + $ invoke project.lint + ``` + +!!! example "Running the linter with all categories" + ``` + $ invoke project.lint --disable="" + ``` + +# Editor/IDE Integration + +## Visual Studio Code + +The project includes a `.vscode/settings.json` file that specifies the `black` formatter and enables an editor ruler at 88 columns. +You may want to configure your own environment to "format on save" with `black` if you find that useful. We are not making that choice at the workspace level. + diff --git a/docs/mkdocs/src/development/contributing.md b/docs/mkdocs/src/development/contributing.md new file mode 100644 index 0000000..a588938 --- /dev/null +++ b/docs/mkdocs/src/development/contributing.md @@ -0,0 +1,11 @@ +CONTRIBUTING TO THE PROJECT +=========================== + +To contribute some code to the project, you will need to submit a GitHub Pull Request (a.k.a PR). Please familiarize yourself with how that works (see [GitHub Pull Requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests)) + +You should follow the project's [code style](code_style.md), and pre-check your code before submitting a PR. The GitHub project is set up with some [Actions](https://github.com/features/actions) that will check that a PR passes at least the basic tests and complies with the coding style, but it is still recommended to check that for yourself before submitting a PR. +To run the basic checks (essentially: running the tests, the linter, and the formatter), use the `project.pre-commit` `invoke` command, and address any issues found: + +``` +$ invoke project.pre-commit +``` diff --git a/docs/mkdocs/src/development/python_environments.md b/docs/mkdocs/src/development/python_environments.md index 0be529e..1aaf05a 100644 --- a/docs/mkdocs/src/development/python_environments.md +++ b/docs/mkdocs/src/development/python_environments.md @@ -1,11 +1,11 @@ PYTHON ENVIRONMENTS =================== -When you don't want to install Bumble in your main/default python environment, +When you don't want to install Bumble in your main/default python environment, using a virtual environment, where the package and its dependencies can be installed, isolated from the rest, may be useful. -There are many flavors of python environments and dependency managers. +There are many flavors of python environments and dependency managers. This page describes a few of the most common ones. @@ -16,7 +16,7 @@ Visit the [`venv` documentation](https://docs.python.org/3/library/venv.html) pa ## Pyenv -`pyenv` lets you easily switch between multiple versions of Python. It's simple, unobtrusive, and follows the UNIX tradition of single-purpose tools that do one thing well. +`pyenv` lets you easily switch between multiple versions of Python. It's simple, unobtrusive, and follows the UNIX tradition of single-purpose tools that do one thing well. Visit the [`pyenv` site](https://github.com/pyenv/pyenv) for instructions on how to install and use `pyenv` @@ -25,10 +25,10 @@ and use `pyenv` Conda is a convenient package manager and virtual environment. The file `environment.yml` is a Conda environment file that you can use to create a new Conda environment. Once created, you can simply activate this environment when -working with Bumble. +working with Bumble. Visit the [Conda site](https://docs.conda.io/en/latest/) for instructions on how to install and use Conda. -A few useful commands: +A few useful commands: ### Create a new `bumble` Conda environment ``` diff --git a/docs/mkdocs/src/examples/index.md b/docs/mkdocs/src/examples/index.md index 9e6285f..ae729ae 100644 --- a/docs/mkdocs/src/examples/index.md +++ b/docs/mkdocs/src/examples/index.md @@ -69,4 +69,4 @@ An app that connects to an RFComm server and bridges the RFComm channel to a loc An app that implements an RFComm server and, when a connection is received, bridges the channel to a local TCP socket ## `run_scanner.py` -An app that scan for BLE devices and print the advertisements received.
\ No newline at end of file +An app that scan for BLE devices and print the advertisements received. diff --git a/docs/mkdocs/src/getting_started.md b/docs/mkdocs/src/getting_started.md index 832691a..b2063d1 100644 --- a/docs/mkdocs/src/getting_started.md +++ b/docs/mkdocs/src/getting_started.md @@ -5,18 +5,18 @@ GETTING STARTED WITH BUMBLE You need Python 3.8 or above. Python >= 3.9 is recommended, but 3.8 should be sufficient if necessary (there may be some optional functionality that will not work on some platforms with -python 3.8). +python 3.8). Visit the [Python site](https://www.python.org/) for instructions on how to install Python -for your platform. +for your platform. Throughout the documentation, when shell commands are shown, it is assumed that you can invoke Python as ``` $ python ``` -If invoking python is different on your platform (it may be `python3` for example, or just `py` or `py.exe`), +If invoking python is different on your platform (it may be `python3` for example, or just `py` or `py.exe`), adjust accordingly. -You may be simply using Bumble as a module for your own application or as a dependency to your own +You may be simply using Bumble as a module for your own application or as a dependency to your own module, or you may be working on modifying or contributing to the Bumble module or example code itself. @@ -65,15 +65,17 @@ $ python -m pip install git+https://github.com/google/bumble.git@27c0551 # Working On The Bumble Code When you work on the Bumble code itself, and run some of the tests or example apps, or import the -module in your own code, you typically either install the package from source in "development mode" as described above, or you may choose to skip the install phase. +module in your own code, you typically either install the package from source in "development mode" as described above, or you may choose to skip the install phase. + +If you plan on contributing to the project, please read the [contributing](development/contributing.md) section. ## Without Installing -If you prefer not to install the package (even in development mode), you can load the module directly from its location in the project. +If you prefer not to install the package (even in development mode), you can load the module directly from its location in the project. A simple way to do that is to set your `PYTHONPATH` to point to the root project directory, where the `bumble` subdirectory is located. You may set `PYTHONPATH` globally, or locally with each command line execution (on Unix-like systems). -Example with a global `PYTHONPATH`, from a unix shell, when the working directory is the root +Example with a global `PYTHONPATH`, from a unix shell, when the working directory is the root directory of the project. ```bash @@ -96,11 +98,11 @@ $ PYTHONPATH=. python examples/run_advertiser.py examples/device1.json serial:/d ``` # Where To Go Next -Once you've installed or downloaded Bumble, you can either start using some of the +Once you've installed or downloaded Bumble, you can either start using some of the [Bundled apps and tools](apps_and_tools/index.md), or look at the [examples](examples/index.md) to get a feel for how to use the APIs, and start writing your own applications. Depending on the use case you're interested in exploring, you may need to use a physical Bluetooth controller, like a USB dongle or a board with a Bluetooth radio. Visit the [Hardware page](hardware/index.md) -for more information on using a physical radio, and/or the [Transports page](transports/index.md) for more +for more information on using a physical radio, and/or the [Transports page](transports/index.md) for more details on interfacing with either hardware modules or virtual controllers over various transports. diff --git a/docs/mkdocs/src/hardware/index.md b/docs/mkdocs/src/hardware/index.md index e088fe1..986532b 100644 --- a/docs/mkdocs/src/hardware/index.md +++ b/docs/mkdocs/src/hardware/index.md @@ -1,18 +1,18 @@ HARDWARE ======== -The Bumble Host connects to a controller over an [HCI Transport](../transports/index.md). -To use a hardware controller attached to the host on which the host application is running, the transport is typically either [HCI over UART](../transports/serial.md) or [HCI over USB](../transports/usb.md). -On Linux, the [VHCI Transport](../transports/vhci.md) can be used to communicate with any controller hardware managed by the operating system. Alternatively, a remote controller (a phyiscal controller attached to a remote host) can be used by connecting one of the networked transports (such as the [TCP Client transport](../transports/tcp_client.md), the [TCP Server transport](../transports/tcp_server.md) or the [UDP Transport](../transports/udp.md)) to an [HCI Bridge](../apps_and_tools/hci_bridge) bridging the network transport to a physical controller on a remote host. +The Bumble Host connects to a controller over an [HCI Transport](../transports/index.md). +To use a hardware controller attached to the host on which the host application is running, the transport is typically either [HCI over UART](../transports/serial.md) or [HCI over USB](../transports/usb.md). +On Linux, the [VHCI Transport](../transports/vhci.md) can be used to communicate with any controller hardware managed by the operating system. Alternatively, a remote controller (a phyiscal controller attached to a remote host) can be used by connecting one of the networked transports (such as the [TCP Client transport](../transports/tcp_client.md), the [TCP Server transport](../transports/tcp_server.md) or the [UDP Transport](../transports/udp.md)) to an [HCI Bridge](../apps_and_tools/hci_bridge) bridging the network transport to a physical controller on a remote host. -In theory, any controller that is compliant with the HCI over UART or HCI over USB protocols can be used. +In theory, any controller that is compliant with the HCI over UART or HCI over USB protocols can be used. -HCI over USB is very common, implemented by a number of commercial Bluetooth dongles. +HCI over USB is very common, implemented by a number of commercial Bluetooth dongles. It is also possible to use an embedded development board, running a specialized application, such as the [`HCI UART`](https://docs.zephyrproject.org/latest/samples/bluetooth/hci_uart/README.html) and [`HCI USB`](https://docs.zephyrproject.org/latest/samples/bluetooth/hci_usb/README.html) demo applications from the [Zephyr project](https://www.zephyrproject.org/), or the [`blehci`](https://mynewt.apache.org/latest/tutorials/ble/blehci_project.html) application from [mynewt/nimble](https://mynewt.apache.org/) Some specific USB dongles and embedded boards that are known to work include: - + * [Nordic nRF52840 DK board](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk) with the Zephyr `HCI UART` application * [Nordic nRF52840 DK board](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk) with the mynewt `blehci` application * [Nordic nrf52840 dongle](https://www.nordicsemi.com/Products/Development-hardware/nRF52840-Dongle) with the Zephyr `HCI USB` application diff --git a/docs/mkdocs/src/images/bumble_layers.svg b/docs/mkdocs/src/images/bumble_layers.svg index 3da9f58..c0dbca1 100644 --- a/docs/mkdocs/src/images/bumble_layers.svg +++ b/docs/mkdocs/src/images/bumble_layers.svg @@ -1 +1 @@ -<svg version="1.1" viewBox="0.0 0.0 539.0209973753281 600.7742782152231" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l539.021 0l0 600.7743l-539.021 0l0 -600.7743z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l539.021 0l0 600.7743l-539.021 0z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m362.34805 205.27522l0 -159.87402" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m362.34805 205.27522l0 -159.87402" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m94.91498 205.27522l0 -159.87402" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m94.91498 205.27522l0 -159.87402" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m131.88086 534.9836l0 -116.81891" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m131.88086 534.9836l0 -116.81891" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m404.9701 534.9836l0 -124.62991" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m404.9701 534.9836l0 -124.62991" fill-rule="evenodd"/><g filter="url(#shadowFilter-p.1)"><use xlink:href="#p.1" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.1" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.1"><path fill="#a4c2f4" d="m4.8992295 210.33295l0 0c0 -8.175705 6.627728 -14.803436 14.803446 -14.803436l497.4482 0c3.9261475 0 7.6914673 1.5596466 10.467651 4.3358307c2.776184 2.776184 4.3358154 6.5414886 4.3358154 10.467606l0 59.212006c0 8.17572 -6.6277466 14.803467 -14.803467 14.803467l-497.4482 0c-8.175717 0 -14.803446 -6.6277466 -14.803446 -14.803467z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 210.33295l0 0c0 -8.175705 6.627728 -14.803436 14.803446 -14.803436l497.4482 0c3.9261475 0 7.6914673 1.5596466 10.467651 4.3358307c2.776184 2.776184 4.3358154 6.5414886 4.3358154 10.467606l0 59.212006c0 8.17572 -6.6277466 14.803467 -14.803467 14.803467l-497.4482 0c-8.175717 0 -14.803446 -6.6277466 -14.803446 -14.803467z" fill-rule="evenodd"/></g><g filter="url(#shadowFilter-p.2)"><use xlink:href="#p.2" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.2" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.2"><path fill="#b6d7a8" d="m4.8992295 13.393214l0 0c0 -5.2562323 4.2610183 -9.517251 9.51725 -9.517251l508.0206 0c2.52417 0 4.9448853 1.0027075 6.7297363 2.787538c1.7848511 1.7848306 2.7875366 4.205581 2.7875366 6.729713l0 38.06786c0 5.256233 -4.2609863 9.51725 -9.517273 9.51725l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.51725z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 13.393214l0 0c0 -5.2562323 4.2610183 -9.517251 9.51725 -9.517251l508.0206 0c2.52417 0 4.9448853 1.0027075 6.7297363 2.787538c1.7848511 1.7848306 2.7875366 4.205581 2.7875366 6.729713l0 38.06786c0 5.256233 -4.2609863 9.51725 -9.517273 9.51725l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.51725z" fill-rule="evenodd"/><path fill="#000000" d="m221.21878 39.34714l5.078125 -13.359375l1.78125 0l5.078125 13.359375l-1.734375 0l-3.671875 -9.984375l-0.53125 -1.4375l-0.0625 0l-0.53125 1.4375l-3.671875 9.984375l-1.734375 0zm5.78125 -3.671875l0 -1.5l3.078125 0l0.546875 1.5l-3.625 0zm-3.625 0l0.546875 -1.5l3.078125 0l0 1.5l-3.625 0zm11.327408 7.703125l0 -13.546875l1.515625 0l0 1.421875l0.078125 0q0.25 -0.453125 0.71875 -0.84375q0.484375 -0.390625 1.125 -0.625q0.640625 -0.25 1.390625 -0.25q1.3125 0 2.328125 0.65625q1.03125 0.65625 1.625 1.796875q0.609375 1.125 0.609375 2.609375q0 1.46875 -0.609375 2.609375q-0.59375 1.140625 -1.625 1.796875q-1.015625 0.640625 -2.328125 0.640625q-1.125 0 -1.984375 -0.515625q-0.859375 -0.53125 -1.25 -1.1875l-0.078125 0l0.078125 1.3125l0 4.125l-1.59375 0zm4.671875 -5.171875q0.8125 0 1.53125 -0.4375q0.71875 -0.4375 1.15625 -1.25q0.4375 -0.828125 0.4375 -1.921875q0 -1.125 -0.4375 -1.9375q-0.4375 -0.8125 -1.15625 -1.25q-0.71875 -0.4375 -1.53125 -0.4375q-0.828125 0 -1.546875 0.4375q-0.71875 0.4375 -1.171875 1.25q-0.4375 0.8125 -0.4375 1.9375q0 1.109375 0.4375 1.921875q0.453125 0.8125 1.171875 1.25q0.71875 0.4375 1.546875 0.4375zm6.372528 5.171875l0 -13.546875l1.515625 0l0 1.421875l0.078125 0q0.25 -0.453125 0.71875 -0.84375q0.484375 -0.390625 1.125 -0.625q0.640625 -0.25 1.390625 -0.25q1.3125 0 2.328125 0.65625q1.03125 0.65625 1.625 1.796875q0.609375 1.125 0.609375 2.609375q0 1.46875 -0.609375 2.609375q-0.59375 1.140625 -1.625 1.796875q-1.015625 0.640625 -2.328125 0.640625q-1.125 0 -1.984375 -0.515625q-0.859375 -0.53125 -1.25 -1.1875l-0.078125 0l0.078125 1.3125l0 4.125l-1.59375 0zm4.671875 -5.171875q0.8125 0 1.53125 -0.4375q0.71875 -0.4375 1.15625 -1.25q0.4375 -0.828125 0.4375 -1.921875q0 -1.125 -0.4375 -1.9375q-0.4375 -0.8125 -1.15625 -1.25q-0.71875 -0.4375 -1.53125 -0.4375q-0.828125 0 -1.546875 0.4375q-0.71875 0.4375 -1.171875 1.25q-0.4375 0.8125 -0.4375 1.9375q0 1.109375 0.4375 1.921875q0.453125 0.8125 1.171875 1.25q0.71875 0.4375 1.546875 0.4375zm6.372513 1.140625l0 -13.359375l1.59375 0l0 13.359375l-1.59375 0zm4.0927124 0l0 -9.515625l1.578125 0l0 9.515625l-1.578125 0zm0.78125 -11.265625q-0.46875 0 -0.8125 -0.328125q-0.328125 -0.34375 -0.328125 -0.8125q0 -0.484375 0.328125 -0.8125q0.34375 -0.328125 0.8125 -0.328125q0.484375 0 0.8125 0.328125q0.328125 0.328125 0.328125 0.8125q0 0.46875 -0.328125 0.8125q-0.328125 0.328125 -0.8125 0.328125zm7.497986 11.5625q-1.40625 0 -2.5 -0.65625q-1.078125 -0.671875 -1.703125 -1.8125q-0.609375 -1.15625 -0.609375 -2.578125q0 -1.46875 0.609375 -2.59375q0.625 -1.140625 1.703125 -1.796875q1.09375 -0.671875 2.5 -0.671875q1.609375 0 2.640625 0.734375q1.03125 0.734375 1.46875 1.890625l-1.4375 0.609375q-0.359375 -0.890625 -1.0625 -1.34375q-0.6875 -0.453125 -1.6875 -0.453125q-0.828125 0 -1.546875 0.453125q-0.71875 0.4375 -1.171875 1.25q-0.453125 0.8125 -0.453125 1.921875q0 1.078125 0.453125 1.90625q0.453125 0.8125 1.171875 1.265625q0.71875 0.4375 1.546875 0.4375q1.015625 0 1.734375 -0.46875q0.734375 -0.46875 1.09375 -1.3125l1.40625 0.59375q-0.46875 1.09375 -1.515625 1.859375q-1.03125 0.765625 -2.640625 0.765625zm8.827332 0q-1.0625 0 -1.875 -0.40625q-0.796875 -0.40625 -1.25 -1.125q-0.453125 -0.71875 -0.453125 -1.640625q0 -1.046875 0.53125 -1.765625q0.546875 -0.71875 1.453125 -1.078125q0.921875 -0.359375 2.015625 -0.359375q0.640625 0 1.171875 0.109375q0.546875 0.09375 0.9375 0.234375q0.40625 0.140625 0.625 0.265625l0 -0.578125q0 -1.078125 -0.765625 -1.703125q-0.765625 -0.640625 -1.875 -0.640625q-0.78125 0 -1.46875 0.34375q-0.671875 0.34375 -1.0625 0.953125l-1.203125 -0.890625q0.375 -0.5625 0.9375 -0.96875q0.5625 -0.40625 1.28125 -0.625q0.71875 -0.234375 1.515625 -0.234375q1.9375 0 3.03125 1.03125q1.109375 1.015625 1.109375 2.75l0 6.03125l-1.5 0l0 -1.359375l-0.078125 0q-0.25 0.40625 -0.703125 0.796875q-0.4375 0.375 -1.046875 0.609375q-0.609375 0.25 -1.328125 0.25zm0.140625 -1.390625q0.828125 0 1.5 -0.40625q0.6875 -0.421875 1.09375 -1.109375q0.421875 -0.6875 0.421875 -1.515625q-0.4375 -0.296875 -1.078125 -0.484375q-0.640625 -0.1875 -1.40625 -0.1875q-1.359375 0 -2.0 0.5625q-0.640625 0.5625 -0.640625 1.375q0 0.78125 0.59375 1.28125q0.609375 0.484375 1.515625 0.484375zm5.876068 -8.421875l5.578125 0l0 1.4375l-5.578125 0l0 -1.4375zm1.671875 7.015625l0 -9.703125l1.578125 0l0 9.3125q0 0.75 0.3125 1.15625q0.3125 0.40625 1.015625 0.40625q0.3125 0 0.578125 -0.09375q0.265625 -0.09375 0.46875 -0.21875l0 1.546875q-0.25 0.109375 -0.546875 0.171875q-0.28125 0.078125 -0.765625 0.078125q-1.1875 0 -1.921875 -0.703125q-0.71875 -0.703125 -0.71875 -1.953125zm6.0434875 2.5l0 -9.515625l1.578125 0l0 9.515625l-1.578125 0zm0.78125 -11.265625q-0.46875 0 -0.8125 -0.328125q-0.328125 -0.34375 -0.328125 -0.8125q0 -0.484375 0.328125 -0.8125q0.34375 -0.328125 0.8125 -0.328125q0.484375 0 0.8125 0.328125q0.328125 0.328125 0.328125 0.8125q0 0.46875 -0.328125 0.8125q-0.328125 0.328125 -0.8125 0.328125zm7.576111 11.5625q-1.4375 0 -2.546875 -0.671875q-1.09375 -0.671875 -1.71875 -1.8125q-0.625 -1.15625 -0.625 -2.5625q0 -1.421875 0.625 -2.5625q0.625 -1.15625 1.71875 -1.828125q1.109375 -0.671875 2.546875 -0.671875q1.4375 0 2.53125 0.6875q1.109375 0.671875 1.734375 1.828125q0.625 1.140625 0.625 2.546875q0 1.40625 -0.625 2.5625q-0.625 1.140625 -1.734375 1.8125q-1.09375 0.671875 -2.53125 0.671875zm0 -1.4375q0.859375 0 1.609375 -0.421875q0.75 -0.4375 1.21875 -1.25q0.46875 -0.8125 0.46875 -1.9375q0 -1.140625 -0.46875 -1.953125q-0.46875 -0.8125 -1.21875 -1.234375q-0.75 -0.4375 -1.609375 -0.4375q-0.859375 0 -1.625 0.4375q-0.765625 0.421875 -1.234375 1.234375q-0.46875 0.8125 -0.46875 1.953125q0 1.125 0.46875 1.9375q0.46875 0.8125 1.234375 1.25q0.765625 0.421875 1.625 0.421875zm6.5418396 1.140625l0 -9.515625l1.515625 0l0 1.40625l0.078125 0q0.375 -0.703125 1.21875 -1.203125q0.84375 -0.5 1.859375 -0.5q1.75 0 2.625 1.015625q0.890625 1.015625 0.890625 2.703125l0 6.09375l-1.578125 0l0 -5.859375q0 -1.375 -0.671875 -1.9375q-0.65625 -0.578125 -1.703125 -0.578125q-0.78125 0 -1.375 0.4375q-0.59375 0.4375 -0.9375 1.125q-0.328125 0.6875 -0.328125 1.453125l0 5.359375l-1.59375 0z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.3)"><use xlink:href="#p.3" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.3" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.3"><path fill="#c9daf8" d="m4.8992295 97.076035l0 0c0 -8.175713 6.627728 -14.803444 14.803446 -14.803444l147.81042 0c3.926117 0 7.691437 1.5596466 10.467621 4.3358307c2.776184 2.776184 4.3358307 6.5414963 4.3358307 10.467613l0 59.212006c0 8.17572 -6.6277313 14.803452 -14.803452 14.803452l-147.81042 0c-8.175717 0 -14.803446 -6.6277313 -14.803446 -14.803452z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 97.076035l0 0c0 -8.175713 6.627728 -14.803444 14.803446 -14.803444l147.81042 0c3.926117 0 7.691437 1.5596466 10.467621 4.3358307c2.776184 2.776184 4.3358307 6.5414963 4.3358307 10.467613l0 59.212006c0 8.17572 -6.6277313 14.803452 -14.803452 14.803452l-147.81042 0c-8.175717 0 -14.803446 -6.6277313 -14.803446 -14.803452z" fill-rule="evenodd"/><path fill="#000000" d="m66.37746 133.60204l0 -13.359367l4.015625 0q2.09375 0 3.609375 0.859375q1.515625 0.84375 2.328125 2.359375q0.828125 1.5 0.828125 3.46875q0 1.9531174 -0.828125 3.4531174q-0.8125 1.5 -2.328125 2.359375q-1.515625 0.859375 -3.609375 0.859375l-4.015625 0zm1.578125 -1.515625l2.4375 0q1.578125 0 2.734375 -0.59375q1.15625 -0.609375 1.78125 -1.765625q0.640625 -1.15625 0.640625 -2.7968674q0 -1.65625 -0.640625 -2.8125q-0.625 -1.15625 -1.78125 -1.75q-1.15625 -0.609375 -2.734375 -0.609375l-2.4375 0l0 10.328117zm15.163467 1.8125q-1.375 0 -2.453125 -0.640625q-1.0625 -0.65625 -1.671875 -1.796875q-0.609375 -1.140625 -0.609375 -2.59375q0 -1.3593674 0.5625 -2.5156174q0.578125 -1.15625 1.609375 -1.859375q1.03125 -0.703125 2.4375 -0.703125q1.421875 0 2.4375 0.625q1.015625 0.625 1.5625 1.734375q0.546875 1.09375 0.546875 2.5156174q0 0.125 -0.015625 0.265625q0 0.125 -0.015625 0.21875l-8.1875 0l0 -1.3124924l6.546875 0q-0.015625 -0.390625 -0.1875 -0.84375q-0.15625 -0.46875 -0.5 -0.859375q-0.34375 -0.40625 -0.875 -0.65625q-0.53125 -0.25 -1.3125 -0.25q-0.9375 0 -1.625 0.484375q-0.671875 0.46875 -1.046875 1.296875q-0.359375 0.8125 -0.359375 1.8593674q0 1.203125 0.46875 2.015625q0.46875 0.796875 1.203125 1.1875q0.75 0.390625 1.546875 0.390625q1.046875 0 1.71875 -0.484375q0.6875 -0.5 1.09375 -1.234375l1.34375 0.65625q-0.5625 1.078125 -1.609375 1.796875q-1.03125 0.703125 -2.609375 0.703125zm8.879303 -0.296875l-3.84375 -9.515617l1.703125 0l2.953125 7.6562424l0.03125 0l2.984375 -7.6562424l1.65625 0l-3.875 9.515617l-1.609375 0zm6.9486847 0l0 -9.515617l1.578125 0l0 9.515617l-1.578125 0zm0.78125 -11.265617q-0.46875 0 -0.8125 -0.328125q-0.328125 -0.34375 -0.328125 -0.8125q0 -0.484375 0.328125 -0.8125q0.34375 -0.328125 0.8125 -0.328125q0.484375 0 0.8125 0.328125q0.328125 0.328125 0.328125 0.8125q0 0.46875 -0.328125 0.8125q-0.328125 0.328125 -0.8125 0.328125zm7.4979706 11.562492q-1.40625 0 -2.5 -0.65625q-1.078125 -0.671875 -1.703125 -1.8125q-0.609375 -1.15625 -0.609375 -2.578125q0 -1.4687424 0.609375 -2.5937424q0.625 -1.140625 1.703125 -1.796875q1.09375 -0.671875 2.5 -0.671875q1.609375 0 2.640625 0.734375q1.03125 0.734375 1.46875 1.890625l-1.4375 0.609375q-0.359375 -0.890625 -1.0625 -1.34375q-0.6875 -0.453125 -1.6875 -0.453125q-0.828125 0 -1.546875 0.453125q-0.71875 0.4375 -1.171875 1.25q-0.453125 0.8125 -0.453125 1.9218674q0 1.078125 0.453125 1.90625q0.453125 0.8125 1.171875 1.265625q0.71875 0.4375 1.546875 0.4375q1.015625 0 1.734375 -0.46875q0.734375 -0.46875 1.09375 -1.3125l1.40625 0.59375q-0.46875 1.09375 -1.515625 1.859375q-1.03125 0.765625 -2.640625 0.765625zm10.014847 0q-1.375 0 -2.453125 -0.640625q-1.0625 -0.65625 -1.671875 -1.796875q-0.609375 -1.140625 -0.609375 -2.59375q0 -1.3593674 0.5625 -2.5156174q0.578125 -1.15625 1.609375 -1.859375q1.03125 -0.703125 2.4375 -0.703125q1.421875 0 2.4375 0.625q1.015625 0.625 1.5625 1.734375q0.546875 1.09375 0.546875 2.5156174q0 0.125 -0.015625 0.265625q0 0.125 -0.015625 0.21875l-8.1875 0l0 -1.3124924l6.546875 0q-0.015625 -0.390625 -0.1875 -0.84375q-0.15625 -0.46875 -0.5 -0.859375q-0.34375 -0.40625 -0.875 -0.65625q-0.53125 -0.25 -1.3125 -0.25q-0.9375 0 -1.625 0.484375q-0.671875 0.46875 -1.046875 1.296875q-0.359375 0.8125 -0.359375 1.8593674q0 1.203125 0.46875 2.015625q0.46875 0.796875 1.203125 1.1875q0.75 0.390625 1.546875 0.390625q1.046875 0 1.71875 -0.484375q0.6875 -0.5 1.09375 -1.234375l1.34375 0.65625q-0.5625 1.078125 -1.609375 1.796875q-1.03125 0.703125 -2.609375 0.703125z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.4)"><use xlink:href="#p.4" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.4" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.4"><path fill="#c9daf8" d="m192.74174 100.78206l0 0c0 -7.7669296 6.296341 -14.063271 14.063278 -14.063271l311.08603 0c3.7298584 0 7.306885 1.4816589 9.944275 4.119034c2.637329 2.6373749 4.1190186 6.214424 4.1190186 9.944237l0 56.251404c0 7.7669373 -6.2963257 14.063278 -14.063293 14.063278l-311.08603 0c-7.7669373 0 -14.063278 -6.296341 -14.063278 -14.063278z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m192.74174 100.78206l0 0c0 -7.7669296 6.296341 -14.063271 14.063278 -14.063271l311.08603 0c3.7298584 0 7.306885 1.4816589 9.944275 4.119034c2.637329 2.6373749 4.1190186 6.214424 4.1190186 9.944237l0 56.251404c0 7.7669373 -6.2963257 14.063278 -14.063293 14.063278l-311.08603 0c-7.7669373 0 -14.063278 -6.296341 -14.063278 -14.063278z" fill-rule="evenodd"/></g><path fill="#a2c4c9" d="m203.9767 122.657425l0 0c0 -4.68219 3.7956696 -8.4778595 8.4778595 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.4778595 -3.7956543 -8.4778595 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m203.9767 122.657425l0 0c0 -4.68219 3.7956696 -8.4778595 8.4778595 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.4778595 -3.7956543 -8.4778595 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m221.41891 145.47264l4.359375 -11.453125l1.53125 0l4.359375 11.453125l-1.5 0l-3.140625 -8.5625l-0.453125 -1.234375l-0.0625 0l-0.453125 1.234375l-3.140625 8.5625l-1.5 0zm4.96875 -3.15625l0 -1.28125l2.640625 0l0.453125 1.28125l-3.09375 0zm-3.109375 0l0.46875 -1.28125l2.640625 0l0 1.28125l-3.109375 0zm9.3450165 3.15625l0 -1.359375q0.046875 -0.046875 0.375 -0.375q0.34375 -0.34375 0.84375 -0.84375q0.5 -0.515625 1.03125 -1.0625q0.546875 -0.546875 1.015625 -1.03125q0.484375 -0.5 0.75 -0.796875q0.5 -0.546875 0.796875 -0.9375q0.3125 -0.390625 0.453125 -0.765625q0.140625 -0.375 0.140625 -0.875q0 -0.46875 -0.25 -0.890625q-0.234375 -0.421875 -0.6875 -0.6875q-0.453125 -0.28125 -1.140625 -0.28125q-0.640625 0 -1.078125 0.28125q-0.4375 0.265625 -0.6875 0.640625q-0.234375 0.375 -0.3125 0.6875l-1.234375 -0.5q0.09375 -0.34375 0.34375 -0.75q0.25 -0.40625 0.65625 -0.78125q0.40625 -0.375 0.984375 -0.625q0.59375 -0.25 1.359375 -0.25q1.046875 0 1.8125 0.453125q0.76564026 0.4375 1.1875153 1.15625q0.421875 0.703125 0.421875 1.53125q0 0.71875 -0.28125 1.375q-0.265625 0.640625 -0.67189026 1.171875q-0.390625 0.53125 -0.796875 0.921875q-0.21875 0.203125 -0.59375 0.578125q-0.359375 0.375 -0.78125 0.796875q-0.40625 0.421875 -0.8125 0.828125q-0.390625 0.390625 -0.6875 0.703125q-0.296875 0.296875 -0.40625 0.390625l0 0l5.1562653 0l0 1.296875l-6.9062653 0zm8.978531 0l0 -11.453125l3.453125 0q1.796875 0 3.09375 0.734375q1.296875 0.71875 2.0 2.015625q0.703125 1.296875 0.703125 2.96875q0 1.6875 -0.703125 2.984375q-0.703125 1.28125 -2.0 2.015625q-1.296875 0.734375 -3.09375 0.734375l-3.453125 0zm1.359375 -1.296875l2.09375 0q1.34375 0 2.328125 -0.515625q1.0 -0.53125 1.546875 -1.515625q0.546875 -0.984375 0.546875 -2.40625q0 -1.40625 -0.546875 -2.390625q-0.546875 -1.0 -1.546875 -1.515625q-0.984375 -0.515625 -2.328125 -0.515625l-2.09375 0l0 8.859375zm9.888641 1.296875l0 -11.453125l3.859375 0q0.953125 0 1.765625 0.4375q0.828125 0.421875 1.3125 1.1875q0.484375 0.75 0.484375 1.765625q0 0.984375 -0.484375 1.765625q-0.484375 0.765625 -1.3125 1.203125q-0.8125 0.421875 -1.765625 0.421875l-3.140625 0l0 -1.296875l3.171875 0q0.671875 0 1.140625 -0.3125q0.484375 -0.3125 0.75 -0.78125q0.265625 -0.484375 0.265625 -1.0q0 -0.515625 -0.265625 -0.984375q-0.265625 -0.484375 -0.75 -0.796875q-0.46875 -0.3125 -1.140625 -0.3125l-2.53125 0l0 10.15625l-1.359375 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m284.8796 122.657425l0 0c0 -4.68219 3.7956543 -8.4778595 8.477844 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.477844 -3.7956543 -8.477844 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m284.8796 122.657425l0 0c0 -4.68219 3.7956543 -8.4778595 8.477844 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.477844 -3.7956543 -8.477844 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m308.7287 145.47264l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.9180298 0l0 -11.453125l6.625 0l0 1.296875l-5.265625 0l0 10.15625l-1.359375 0zm0.71875 -4.953125l0 -1.28125l5.390625 0l0 1.28125l-5.390625 0zm7.777252 4.953125l0 -11.453125l3.859375 0q0.953125 0 1.765625 0.4375q0.828125 0.421875 1.3125 1.1875q0.484375 0.75 0.484375 1.765625q0 0.984375 -0.484375 1.765625q-0.484375 0.765625 -1.3125 1.203125q-0.8125 0.421875 -1.765625 0.421875l-3.140625 0l0 -1.296875l3.171875 0q0.671875 0 1.140625 -0.3125q0.484375 -0.3125 0.75 -0.78125q0.265625 -0.484375 0.265625 -1.0q0 -0.515625 -0.265625 -0.984375q-0.265625 -0.484375 -0.75 -0.796875q-0.46875 -0.3125 -1.140625 -0.3125l-2.53125 0l0 10.15625l-1.359375 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m365.78247 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m365.78247 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m390.90356 145.47264l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.9180298 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.9200134 0l0 -11.453125l3.453125 0q1.796875 0 3.09375 0.734375q1.296875 0.71875 2.0 2.015625q0.703125 1.296875 0.703125 2.96875q0 1.6875 -0.703125 2.984375q-0.703125 1.28125 -2.0 2.015625q-1.296875 0.734375 -3.09375 0.734375l-3.453125 0zm1.359375 -1.296875l2.09375 0q1.34375 0 2.328125 -0.515625q1.0 -0.53125 1.546875 -1.515625q0.546875 -0.984375 0.546875 -2.40625q0 -1.40625 -0.546875 -2.390625q-0.546875 -1.0 -1.546875 -1.515625q-0.984375 -0.515625 -2.328125 -0.515625l-2.09375 0l0 8.859375z" fill-rule="nonzero"/><g filter="url(#shadowFilter-p.5)"><use xlink:href="#p.5" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.5" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.5"><path fill="#6d9eeb" d="m4.8992295 317.66617l0 0c0 -5.2562256 4.2610183 -9.517242 9.51725 -9.517242l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.205597 2.7875366 6.729706l0 38.06787c0 5.2562256 -4.2609863 9.517242 -9.517273 9.517242l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.517242z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 317.66617l0 0c0 -5.2562256 4.2610183 -9.517242 9.51725 -9.517242l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.205597 2.7875366 6.729706l0 38.06787c0 5.2562256 -4.2609863 9.517242 -9.517273 9.517242l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.517242z" fill-rule="evenodd"/><path fill="#000000" d="m252.81201 342.56012l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm7.242401 0.25q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm8.549774 1.21875q-0.921875 0 -1.625 -0.296875q-0.6875 -0.296875 -1.140625 -0.796875q-0.453125 -0.5 -0.671875 -1.09375l1.203125 -0.546875q0.328125 0.734375 0.9375 1.140625q0.609375 0.40625 1.390625 0.40625q0.75 0 1.25 -0.296875q0.515625 -0.3125 0.515625 -0.90625q0 -0.375 -0.21875 -0.625q-0.203125 -0.25 -0.609375 -0.421875q-0.390625 -0.171875 -0.96875 -0.3125l-1.0 -0.265625q-0.5625 -0.15625 -1.078125 -0.4375q-0.515625 -0.296875 -0.828125 -0.75q-0.3125 -0.453125 -0.3125 -1.109375q0 -0.734375 0.421875 -1.265625q0.4375 -0.53125 1.140625 -0.8125q0.703125 -0.28125 1.515625 -0.28125q0.703125 0 1.3125 0.203125q0.625 0.203125 1.078125 0.59375q0.46875 0.390625 0.703125 0.96875l-1.171875 0.546875q-0.3125 -0.609375 -0.828125 -0.84375q-0.5 -0.25 -1.125 -0.25q-0.671875 0 -1.171875 0.296875q-0.5 0.296875 -0.5 0.8125q0 0.515625 0.40625 0.765625q0.40625 0.25 1.0 0.421875l1.1875 0.296875q1.203125 0.3125 1.8125 0.90625q0.609375 0.59375 0.609375 1.46875q0 0.765625 -0.4375 1.328125q-0.4375 0.546875 -1.171875 0.859375q-0.734375 0.296875 -1.625 0.296875zm4.0817566 -8.40625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875z" fill-rule="nonzero"/></g><path fill="#a2c4c9" d="m141.82906 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m141.82906 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m162.77774 250.34612q-0.90625 0 -1.71875 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.609375 -0.609375 -0.953125 -1.421875q-0.34375 -0.828125 -0.34375 -1.78125q0 -0.953125 0.34375 -1.765625q0.34375 -0.828125 0.953125 -1.4375q0.609375 -0.609375 1.421875 -0.9375q0.8125 -0.34375 1.71875 -0.34375q0.953125 0 1.78125 0.34375q0.84375 0.328125 1.375 0.9375l-0.71875 0.71875q-0.265625 -0.328125 -0.65625 -0.5625q-0.375 -0.234375 -0.828125 -0.34375q-0.4375 -0.125 -0.9375 -0.125q-0.6875 0 -1.3125 0.25q-0.609375 0.25 -1.09375 0.71875q-0.46875 0.453125 -0.75 1.109375q-0.265625 0.640625 -0.265625 1.4375q0 0.8125 0.28125 1.453125q0.28125 0.640625 0.75 1.109375q0.484375 0.46875 1.09375 0.71875q0.625 0.234375 1.296875 0.234375q0.578125 0 1.125 -0.15625q0.546875 -0.171875 0.984375 -0.5q0.4375 -0.34375 0.71875 -0.890625q0.296875 -0.546875 0.359375 -1.296875l-3.171875 0l0 -0.953125l4.125 0q0.03125 0.15625 0.046875 0.3125q0.015625 0.140625 0.015625 0.3125l0 0l0 0l0 0q0 0.921875 -0.3125 1.6875q-0.3125 0.765625 -0.875 1.3125q-0.5625 0.546875 -1.328125 0.84375q-0.765625 0.296875 -1.703125 0.296875zm4.551193 -0.1875l3.25 -8.59375l1.15625 0l3.265625 8.59375l-1.109375 0l-2.375 -6.421875l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.421875l-1.109375 0zm3.71875 -2.359375l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm8.400681 2.359375l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0zm8.862289 7.625l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0z" fill-rule="nonzero"/><path fill="#000000" d="m161.77573 264.3461q-0.9375 0 -1.75 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.59375 -0.609375 -0.9375 -1.421875q-0.328125 -0.828125 -0.328125 -1.78125q0 -0.953125 0.328125 -1.765625q0.34375 -0.828125 0.9375 -1.421875q0.609375 -0.609375 1.421875 -0.95310974q0.8125 -0.34375 1.75 -0.34375q0.671875 0 1.234375 0.171875q0.578125 0.15625 1.046875 0.46873474q0.484375 0.3125 0.875 0.765625l-0.734375 0.703125q-0.328125 -0.390625 -0.703125 -0.640625q-0.359375 -0.265625 -0.78125 -0.375q-0.421875 -0.125 -0.9375 -0.125q-0.921875 0 -1.703125 0.4375q-0.765625 0.421875 -1.234375 1.21875q-0.46875 0.78125 -0.46875 1.859375q0 1.0625 0.46875 1.859375q0.46875 0.796875 1.234375 1.234375q0.78125 0.421875 1.703125 0.421875q0.578125 0 1.046875 -0.15625q0.46875 -0.15625 0.875 -0.4375q0.40625 -0.296875 0.734375 -0.6875l0.75 0.71875q-0.375 0.4375 -0.90625 0.796875q-0.515625 0.34375 -1.140625 0.546875q-0.625 0.1875 -1.359375 0.1875zm4.537689 -0.1875l0 -8.593735l1.03125 0l0 8.593735l-1.03125 0zm2.641388 0l0 -6.125l1.015625 0l0 6.125l-1.015625 0zm0.5 -7.25q-0.296875 0 -0.515625 -0.21875q-0.21875 -0.21875 -0.21875 -0.515625q0 -0.31248474 0.21875 -0.51560974q0.21875 -0.21875 0.515625 -0.21875q0.3125 0 0.515625 0.21875q0.21875 0.203125 0.21875 0.51560974q0 0.296875 -0.21875 0.515625q-0.203125 0.21875 -0.515625 0.21875zm4.7770233 7.4375q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.90625l0.046875 0q0.234375 -0.4375 0.78125 -0.765625q0.546875 -0.328125 1.1875 -0.328125q1.140625 0 1.703125 0.65625q0.578125 0.65625 0.578125 1.734375l0 3.921875l-1.03125 0l0 -3.765625q0 -0.890625 -0.421875 -1.25q-0.421875 -0.375 -1.09375 -0.375q-0.515625 0 -0.90625 0.28125q-0.375 0.28125 -0.59375 0.734375q-0.203125 0.4375 -0.203125 0.9375l0 3.4375l-1.03125 0zm6.0257874 -6.125l3.578125 0l0 0.921875l-3.578125 0l0 -0.921875zm1.0625 4.515625l0 -6.234375l1.015625 0l0 5.984375q0 0.484375 0.1875 0.75q0.203125 0.25 0.671875 0.25q0.203125 0 0.359375 -0.046875q0.171875 -0.0625 0.3125 -0.15625l0 1.0q-0.15625 0.078125 -0.34375 0.109375q-0.1875 0.046875 -0.5 0.046875q-0.765625 0 -1.234375 -0.4375q-0.46875 -0.453125 -0.46875 -1.265625z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m77.69106 235.04298l0 0c0 -4.8590393 3.9390335 -8.798065 8.798073 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499481 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798073 -3.9390259 -8.798073 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m77.69106 235.04298l0 0c0 -4.8590393 3.9390335 -8.798065 8.798073 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499481 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798073 -3.9390259 -8.798073 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m101.24976 257.3461q-0.90625 0 -1.71875 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.609375 -0.60935974 -0.953125 -1.4218597q-0.34375 -0.828125 -0.34375 -1.78125q0 -0.953125 0.34375 -1.765625q0.34375 -0.828125 0.953125 -1.4375q0.609375 -0.609375 1.421875 -0.9375q0.8125 -0.34375 1.71875 -0.34375q0.953125 0 1.78125 0.34375q0.84375 0.328125 1.375 0.9375l-0.71875 0.71875q-0.265625 -0.328125 -0.65625 -0.5625q-0.375 -0.234375 -0.828125 -0.34375q-0.4375 -0.125 -0.9375 -0.125q-0.6875 0 -1.3125 0.25q-0.609375 0.25 -1.09375 0.71875q-0.46875 0.453125 -0.75 1.109375q-0.265625 0.640625 -0.265625 1.4375q0 0.8125 0.28125 1.453125q0.28125 0.640625 0.75 1.109375q0.484375 0.46875 1.09375 0.71873474q0.625 0.234375 1.296875 0.234375q0.578125 0 1.125 -0.15625q0.546875 -0.171875 0.984375 -0.49998474q0.4375 -0.34375 0.71875 -0.890625q0.296875 -0.546875 0.359375 -1.296875l-3.171875 0l0 -0.953125l4.125 0q0.03125 0.15625 0.046875 0.3125q0.015625 0.140625 0.015625 0.3125l0 0l0 0l0 0q0 0.921875 -0.3125 1.6875q-0.3125 0.765625 -0.875 1.3124847q-0.5625 0.546875 -1.328125 0.84375q-0.765625 0.296875 -1.703125 0.296875zm4.551193 -0.1875l3.25 -8.593735l1.15625 0l3.265625 8.593735l-1.109375 0l-2.375 -6.4218597l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.4218597l-1.109375 0zm3.71875 -2.3593597l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm7.4150543 2.3593597l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.75 -0.375 1.328125q-0.359375 0.5625 -0.96875 0.890625q-0.609375 0.328125 -1.328125 0.328125l-2.359375 0l0 -0.96875l2.375 0q0.5 0 0.859375 -0.234375q0.359375 -0.234375 0.5625 -0.59375q0.203125 -0.359375 0.203125 -0.75q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m205.96704 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.85266 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m205.96704 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.85266 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m226.91573 250.34612q-0.90625 0 -1.71875 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.609375 -0.609375 -0.953125 -1.421875q-0.34375 -0.828125 -0.34375 -1.78125q0 -0.953125 0.34375 -1.765625q0.34375 -0.828125 0.953125 -1.4375q0.609375 -0.609375 1.421875 -0.9375q0.8125 -0.34375 1.71875 -0.34375q0.953125 0 1.78125 0.34375q0.84375 0.328125 1.375 0.9375l-0.71875 0.71875q-0.265625 -0.328125 -0.65625 -0.5625q-0.375 -0.234375 -0.828125 -0.34375q-0.4375 -0.125 -0.9375 -0.125q-0.6875 0 -1.3125 0.25q-0.609375 0.25 -1.09375 0.71875q-0.46875 0.453125 -0.75 1.109375q-0.265625 0.640625 -0.265625 1.4375q0 0.8125 0.28125 1.453125q0.28125 0.640625 0.75 1.109375q0.484375 0.46875 1.09375 0.71875q0.625 0.234375 1.296875 0.234375q0.578125 0 1.125 -0.15625q0.546875 -0.171875 0.984375 -0.5q0.4375 -0.34375 0.71875 -0.890625q0.296875 -0.546875 0.359375 -1.296875l-3.171875 0l0 -0.953125l4.125 0q0.03125 0.15625 0.046875 0.3125q0.015625 0.140625 0.015625 0.3125l0 0l0 0l0 0q0 0.921875 -0.3125 1.6875q-0.3125 0.765625 -0.875 1.3125q-0.5625 0.546875 -1.328125 0.84375q-0.765625 0.296875 -1.703125 0.296875zm4.551193 -0.1875l3.25 -8.59375l1.15625 0l3.265625 8.59375l-1.109375 0l-2.375 -6.421875l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.421875l-1.109375 0zm3.71875 -2.359375l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm8.400681 2.359375l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0zm8.862289 7.625l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0z" fill-rule="nonzero"/><path fill="#000000" d="m222.3267 264.3461q-0.625 0 -1.234375 -0.265625q-0.609375 -0.265625 -1.078125 -0.78125q-0.46875 -0.515625 -0.6875 -1.28125l0.96875 -0.390625q0.203125 0.75 0.734375 1.25q0.546875 0.5 1.328125 0.5q0.46875 0 0.875 -0.171875q0.40625 -0.171875 0.640625 -0.5q0.25 -0.34375 0.25 -0.828125q0 -0.4375 -0.21875 -0.734375q-0.203125 -0.296875 -0.625 -0.53125q-0.40625 -0.234375 -1.03125 -0.453125l-0.53125 -0.1875q-0.359375 -0.125 -0.734375 -0.3125q-0.359375 -0.1875 -0.671875 -0.453125q-0.296875 -0.28125 -0.484375 -0.65625q-0.171875 -0.375 -0.171875 -0.890625q0 -0.625 0.328125 -1.140625q0.34375 -0.515625 0.921875 -0.82810974q0.59375 -0.3125 1.359375 -0.3125q0.8125 0 1.359375 0.296875q0.546875 0.28125 0.859375 0.68748474q0.3125 0.40625 0.421875 0.734375l-0.953125 0.421875q-0.0625 -0.265625 -0.265625 -0.53125q-0.203125 -0.265625 -0.546875 -0.453125q-0.34375 -0.1875 -0.859375 -0.1875q-0.421875 0 -0.796875 0.171875q-0.359375 0.171875 -0.578125 0.46875q-0.21875 0.28125 -0.21875 0.671875q0 0.53125 0.421875 0.84375q0.4375 0.3125 1.171875 0.5625l0.5625 0.203125q0.390625 0.125 0.78125 0.328125q0.40625 0.1875 0.75 0.484375q0.359375 0.296875 0.5625 0.734375q0.21875 0.4375 0.21875 1.046875q0 0.671875 -0.265625 1.140625q-0.25 0.46875 -0.671875 0.78125q-0.40625 0.296875 -0.90625 0.421875q-0.5 0.140625 -0.984375 0.140625zm6.869049 0q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0zm6.5165253 0l-2.484375 -6.125l1.09375 0l1.90625 4.921875l0.015625 0l1.921875 -4.921875l1.0625 0l-2.484375 6.125l-1.03125 0zm6.9099274 0.1875q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m270.105 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499329 1.6499634 2.5768738 3.8877869 2.5768738 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85266 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m270.105 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499329 1.6499634 2.5768738 3.8877869 2.5768738 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85266 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m281.83264 250.34612q-0.625 0 -1.234375 -0.265625q-0.609375 -0.265625 -1.078125 -0.78125q-0.46875 -0.515625 -0.6875 -1.28125l0.96875 -0.390625q0.203125 0.75 0.734375 1.25q0.546875 0.5 1.328125 0.5q0.46875 0 0.875 -0.171875q0.40625 -0.171875 0.640625 -0.5q0.25 -0.34375 0.25 -0.828125q0 -0.4375 -0.21875 -0.734375q-0.203125 -0.296875 -0.625 -0.53125q-0.40625 -0.234375 -1.03125 -0.453125l-0.53125 -0.1875q-0.359375 -0.125 -0.734375 -0.3125q-0.359375 -0.1875 -0.671875 -0.453125q-0.296875 -0.28125 -0.484375 -0.65625q-0.171875 -0.375 -0.171875 -0.890625q0 -0.625 0.328125 -1.140625q0.34375 -0.515625 0.921875 -0.828125q0.59375 -0.3125 1.359375 -0.3125q0.8125 0 1.359375 0.296875q0.546875 0.28125 0.859375 0.6875q0.3125 0.40625 0.421875 0.734375l-0.953125 0.421875q-0.0625 -0.265625 -0.265625 -0.53125q-0.203125 -0.265625 -0.546875 -0.453125q-0.34375 -0.1875 -0.859375 -0.1875q-0.421875 0 -0.796875 0.171875q-0.359375 0.171875 -0.578125 0.46875q-0.21875 0.28125 -0.21875 0.671875q0 0.53125 0.421875 0.84375q0.4375 0.3125 1.171875 0.5625l0.5625 0.203125q0.390625 0.125 0.78125 0.328125q0.40625 0.1875 0.75 0.484375q0.359375 0.296875 0.5625 0.734375q0.21875 0.4375 0.21875 1.046875q0 0.671875 -0.265625 1.140625q-0.25 0.46875 -0.671875 0.78125q-0.40625 0.296875 -0.90625 0.421875q-0.5 0.140625 -0.984375 0.140625zm6.869049 0q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm6.862915 0q-0.90625 0 -1.609375 -0.421875q-0.703125 -0.421875 -1.09375 -1.15625q-0.390625 -0.75 -0.390625 -1.671875q0 -0.9375 0.390625 -1.65625q0.390625 -0.734375 1.09375 -1.15625q0.703125 -0.4375 1.609375 -0.4375q1.03125 0 1.6875 0.484375q0.671875 0.46875 0.953125 1.203125l-0.921875 0.390625q-0.234375 -0.5625 -0.6875 -0.859375q-0.453125 -0.296875 -1.078125 -0.296875q-0.546875 0 -1.015625 0.296875q-0.453125 0.28125 -0.75 0.8125q-0.28125 0.515625 -0.28125 1.21875q0 0.703125 0.28125 1.234375q0.296875 0.515625 0.75 0.8125q0.46875 0.28125 1.015625 0.28125q0.640625 0 1.109375 -0.296875q0.46875 -0.296875 0.703125 -0.859375l0.90625 0.390625q-0.296875 0.703125 -0.96875 1.203125q-0.671875 0.484375 -1.703125 0.484375zm5.989563 0q-1.125 0 -1.703125 -0.65625q-0.5625 -0.65625 -0.5625 -1.796875l0 -3.859375l1.03125 0l0 3.703125q0 0.90625 0.40625 1.296875q0.421875 0.390625 1.046875 0.390625q0.546875 0 0.9375 -0.28125q0.40625 -0.28125 0.609375 -0.71875q0.21875 -0.453125 0.21875 -0.921875l0 -3.46875l1.015625 0l0 6.125l-0.96875 0l0 -0.890625l-0.046875 0q-0.15625 0.296875 -0.46875 0.546875q-0.296875 0.234375 -0.6875 0.375q-0.390625 0.15625 -0.828125 0.15625zm4.513275 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0zm4.6094055 0l0 -6.125l1.015625 0l0 6.125l-1.015625 0zm0.5 -7.25q-0.296875 0 -0.515625 -0.21875q-0.21875 -0.21875 -0.21875 -0.515625q0 -0.3125 0.21875 -0.515625q0.21875 -0.21875 0.515625 -0.21875q0.3125 0 0.515625 0.21875q0.21875 0.203125 0.21875 0.515625q0 0.296875 -0.21875 0.515625q-0.203125 0.21875 -0.515625 0.21875zm1.6364136 1.125l3.578125 0l0 0.921875l-3.578125 0l0 -0.921875zm1.0625 4.515625l0 -6.234375l1.015625 0l0 5.984375q0 0.484375 0.1875 0.75q0.203125 0.25 0.671875 0.25q0.203125 0 0.359375 -0.046875q0.171875 -0.0625 0.3125 -0.15625l0 1.0q-0.15625 0.078125 -0.34375 0.109375q-0.1875 0.046875 -0.5 0.046875q-0.765625 0 -1.234375 -0.4375q-0.46875 -0.453125 -0.46875 -1.265625zm5.549408 3.890625q-0.046875 0.078125 -0.09375 0.171875q-0.046875 0.109375 -0.0625 0.140625l-1.046875 0q0.0625 -0.15625 0.140625 -0.34375q0.09375 -0.1875 0.21875 -0.453125q0.0625 -0.15625 0.125 -0.296875q0.0625 -0.125 0.140625 -0.28125q0.078125 -0.15625 0.1875 -0.40625l0.84375 -1.828125l0.21875 -0.5625l1.859375 -4.546875l1.09375 0l-3.171875 7.3125q-0.046875 0.109375 -0.15625 0.34375q-0.09375 0.234375 -0.1875 0.453125q-0.078125 0.21875 -0.109375 0.296875zm0.21875 -2.53125l-2.59375 -5.875l1.109375 0l1.921875 4.546875l0.09375 0l-0.53125 1.328125z" fill-rule="nonzero"/><path fill="#000000" d="m277.8184 264.1586l0 -8.593735l1.484375 0l2.734375 7.0468597l0.046875 0l2.734375 -7.0468597l1.484375 0l0 8.593735l-1.015625 0l0 -5.453125l0.046875 -1.640625l-0.046875 0l-2.8125 7.09375l-0.828125 0l-2.8125 -7.09375l-0.046875 0l0.046875 1.640625l0 5.453125l-1.015625 0zm11.977722 0.1875q-0.6875 0 -1.203125 -0.265625q-0.515625 -0.265625 -0.8125 -0.71875q-0.296875 -0.46875 -0.296875 -1.046875q0 -0.671875 0.34375 -1.125q0.359375 -0.46875 0.9375 -0.703125q0.59375 -0.234375 1.296875 -0.234375q0.40625 0 0.75 0.0625q0.359375 0.0625 0.609375 0.15625q0.265625 0.09375 0.390625 0.171875l0 -0.375q0 -0.6875 -0.5 -1.09375q-0.484375 -0.421875 -1.1875 -0.421875q-0.5 0 -0.9375 0.234375q-0.4375 0.21875 -0.6875 0.609375l-0.78125 -0.578125q0.25 -0.359375 0.609375 -0.625q0.359375 -0.265625 0.8125 -0.40625q0.46875 -0.140625 0.984375 -0.140625q1.25 0 1.953125 0.671875q0.71875 0.65625 0.71875 1.765625l0 3.875l-0.984375 0l0 -0.875l-0.046875 0q-0.15625 0.265625 -0.453125 0.515625q-0.28125 0.234375 -0.671875 0.390625q-0.375 0.15625 -0.84375 0.15625zm0.09375 -0.890625q0.53125 0 0.96875 -0.265625q0.4375 -0.265625 0.6875 -0.703125q0.265625 -0.453125 0.265625 -0.984375q-0.265625 -0.1875 -0.6875 -0.296875q-0.40625 -0.125 -0.90625 -0.125q-0.875 0 -1.28125 0.359375q-0.40625 0.359375 -0.40625 0.875q0 0.515625 0.375 0.828125q0.390625 0.3125 0.984375 0.3125zm4.30954 0.703125l0 -6.125l0.984375 0l0 0.90625l0.046875 0q0.234375 -0.4375 0.78125 -0.765625q0.546875 -0.328125 1.1875 -0.328125q1.140625 0 1.703125 0.65625q0.578125 0.65625 0.578125 1.734375l0 3.921875l-1.03125 0l0 -3.765625q0 -0.890625 -0.421875 -1.25q-0.421875 -0.375 -1.09375 -0.375q-0.515625 0 -0.90625 0.28125q-0.375 0.28125 -0.59375 0.734375q-0.203125 0.4375 -0.203125 0.9375l0 3.4375l-1.03125 0zm8.640778 0.1875q-0.6875 0 -1.203125 -0.265625q-0.515625 -0.265625 -0.8125 -0.71875q-0.296875 -0.46875 -0.296875 -1.046875q0 -0.671875 0.34375 -1.125q0.359375 -0.46875 0.9375 -0.703125q0.59375 -0.234375 1.296875 -0.234375q0.40625 0 0.75 0.0625q0.359375 0.0625 0.609375 0.15625q0.265625 0.09375 0.390625 0.171875l0 -0.375q0 -0.6875 -0.5 -1.09375q-0.484375 -0.421875 -1.1875 -0.421875q-0.5 0 -0.9375 0.234375q-0.4375 0.21875 -0.6875 0.609375l-0.78125 -0.578125q0.25 -0.359375 0.609375 -0.625q0.359375 -0.265625 0.8125 -0.40625q0.46875 -0.140625 0.984375 -0.140625q1.25 0 1.953125 0.671875q0.71875 0.65625 0.71875 1.765625l0 3.875l-0.984375 0l0 -0.875l-0.046875 0q-0.15625 0.265625 -0.453125 0.515625q-0.28125 0.234375 -0.671875 0.390625q-0.375 0.15625 -0.84375 0.15625zm0.09375 -0.890625q0.53125 0 0.96875 -0.265625q0.4375 -0.265625 0.6875 -0.703125q0.265625 -0.453125 0.265625 -0.984375q-0.265625 -0.1875 -0.6875 -0.296875q-0.40625 -0.125 -0.90625 -0.125q-0.875 0 -1.28125 0.359375q-0.40625 0.359375 -0.40625 0.875q0 0.515625 0.375 0.828125q0.390625 0.3125 0.984375 0.3125zm6.937042 3.484375q-0.78125 0 -1.34375 -0.265625q-0.546875 -0.25 -0.890625 -0.65625q-0.34375 -0.390625 -0.484375 -0.796875l0.9375 -0.390625q0.1875 0.5 0.640625 0.84375q0.46875 0.34375 1.140625 0.34375q0.96875 0 1.484375 -0.5625q0.515625 -0.546875 0.515625 -1.546875l0 -0.6875l-0.046875 0q-0.296875 0.453125 -0.84375 0.765625q-0.53125 0.296875 -1.25 0.296875q-0.78125 0 -1.4375 -0.40625q-0.65625 -0.421875 -1.046875 -1.140625q-0.375 -0.71875 -0.375 -1.671875q0 -0.953125 0.375 -1.671875q0.390625 -0.734375 1.046875 -1.140625q0.65625 -0.40625 1.4375 -0.40625q0.71875 0 1.25 0.3125q0.546875 0.296875 0.84375 0.75l0.046875 0l0 -0.875l0.96875 0l0 5.890625q0 1.015625 -0.390625 1.6875q-0.390625 0.671875 -1.0625 1.0q-0.65625 0.328125 -1.515625 0.328125zm0 -3.578125q0.546875 0 1.0 -0.265625q0.453125 -0.28125 0.71875 -0.796875q0.28125 -0.515625 0.28125 -1.234375q0 -0.75 -0.28125 -1.25q-0.265625 -0.515625 -0.71875 -0.78125q-0.453125 -0.265625 -1.0 -0.265625q-0.53125 0 -1.0 0.28125q-0.453125 0.265625 -0.734375 0.78125q-0.265625 0.5 -0.265625 1.234375q0 0.734375 0.265625 1.25q0.28125 0.5 0.734375 0.78125q0.46875 0.265625 1.0 0.265625zm7.15094 0.984375q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m13.553075 235.04298l0 0c0 -4.8590393 3.9390326 -8.798065 8.798076 -8.798065l43.85267 0c2.3333893 0 4.5712204 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.5768967 3.8877869 2.5768967 6.221176l0 35.19124c0 4.85907 -3.9390335 8.798096 -8.798073 8.798096l-43.85267 0c-4.859043 0 -8.798076 -3.9390259 -8.798076 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m13.553075 235.04298l0 0c0 -4.8590393 3.9390326 -8.798065 8.798076 -8.798065l43.85267 0c2.3333893 0 4.5712204 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.5768967 3.8877869 2.5768967 6.221176l0 35.19124c0 4.85907 -3.9390335 8.798096 -8.798073 8.798096l-43.85267 0c-4.859043 0 -8.798076 -3.9390259 -8.798076 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m27.098484 257.1586l0 -8.593735l1.03125 0l0 7.6249847l3.75 0l0 0.96875l-4.78125 0zm5.7017975 0l0 -1.015625q0.03125 -0.046875 0.28125 -0.29685974q0.25 -0.25 0.625 -0.625q0.375 -0.375 0.78125 -0.78125q0.40625 -0.421875 0.75 -0.78125q0.359375 -0.375 0.5625 -0.609375q0.375 -0.40625 0.59375 -0.6875q0.234375 -0.296875 0.34375 -0.578125q0.109375 -0.28125 0.109375 -0.65625q0 -0.359375 -0.1875 -0.671875q-0.171875 -0.3125 -0.515625 -0.515625q-0.34375 -0.203125 -0.859375 -0.203125q-0.46875 0 -0.8125 0.203125q-0.328125 0.203125 -0.515625 0.484375q-0.171875 0.28125 -0.234375 0.5l-0.921875 -0.359375q0.078125 -0.265625 0.265625 -0.5625q0.1875 -0.3125 0.484375 -0.59375q0.3125 -0.28125 0.75 -0.46875q0.4375 -0.1875 1.015625 -0.1875q0.78125 0 1.34375 0.34375q0.578125 0.328125 0.890625 0.859375q0.328125 0.53125 0.328125 1.15625q0 0.53125 -0.203125 1.03125q-0.203125 0.484375 -0.5 0.875q-0.296875 0.390625 -0.609375 0.703125q-0.15625 0.15625 -0.4375 0.4375q-0.28125 0.265625 -0.59375 0.59375q-0.3125 0.3125 -0.609375 0.609375q-0.296875 0.296875 -0.515625 0.53125q-0.21875 0.21873474 -0.3125 0.29685974l0 0l3.875 0l0 0.96875l-5.171875 0zm10.749542 0.1875q-0.9375 0 -1.75 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.59375 -0.60935974 -0.9375 -1.4218597q-0.328125 -0.828125 -0.328125 -1.78125q0 -0.953125 0.328125 -1.765625q0.34375 -0.828125 0.9375 -1.421875q0.609375 -0.609375 1.421875 -0.953125q0.8125 -0.34375 1.75 -0.34375q0.671875 0 1.234375 0.171875q0.578125 0.15625 1.046875 0.46875q0.484375 0.3125 0.875 0.765625l-0.734375 0.703125q-0.328125 -0.390625 -0.703125 -0.640625q-0.359375 -0.265625 -0.78125 -0.375q-0.421875 -0.125 -0.9375 -0.125q-0.921875 0 -1.703125 0.4375q-0.765625 0.421875 -1.234375 1.21875q-0.46875 0.78125 -0.46875 1.859375q0 1.0625 0.46875 1.859375q0.46875 0.796875 1.234375 1.234375q0.78125 0.42185974 1.703125 0.42185974q0.578125 0 1.046875 -0.15625q0.46875 -0.15625 0.875 -0.43748474q0.40625 -0.296875 0.734375 -0.6875l0.75 0.71875q-0.375 0.43748474 -0.90625 0.79685974q-0.515625 0.34375 -1.140625 0.546875q-0.625 0.1875 -1.359375 0.1875zm4.0951843 -0.1875l3.25 -8.593735l1.15625 0l3.265625 8.593735l-1.109375 0l-2.375 -6.4218597l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.4218597l-1.109375 0zm3.71875 -2.3593597l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm7.4150543 2.3593597l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.75 -0.375 1.328125q-0.359375 0.5625 -0.96875 0.890625q-0.609375 0.328125 -1.328125 0.328125l-2.359375 0l0 -0.96875l2.375 0q0.5 0 0.859375 -0.234375q0.359375 -0.234375 0.5625 -0.59375q0.203125 -0.359375 0.203125 -0.75q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m334.243 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85269 0c2.333374 0 4.5711975 0.92692566 6.221161 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85269 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m334.243 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85269 0c2.333374 0 4.5711975 0.92692566 6.221161 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85269 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m357.28073 257.3461q-0.625 0 -1.234375 -0.265625q-0.609375 -0.265625 -1.078125 -0.78125q-0.46875 -0.51560974 -0.6875 -1.2812347l0.96875 -0.390625q0.203125 0.75 0.734375 1.25q0.546875 0.49998474 1.328125 0.49998474q0.46875 0 0.875 -0.171875q0.40625 -0.171875 0.640625 -0.49998474q0.25 -0.34375 0.25 -0.828125q0 -0.4375 -0.21875 -0.734375q-0.203125 -0.296875 -0.625 -0.53125q-0.40625 -0.234375 -1.03125 -0.453125l-0.53125 -0.1875q-0.359375 -0.125 -0.734375 -0.3125q-0.359375 -0.1875 -0.671875 -0.453125q-0.296875 -0.28125 -0.484375 -0.65625q-0.171875 -0.375 -0.171875 -0.890625q0 -0.625 0.328125 -1.140625q0.34375 -0.515625 0.921875 -0.828125q0.59375 -0.3125 1.359375 -0.3125q0.8125 0 1.359375 0.296875q0.546875 0.28125 0.859375 0.6875q0.3125 0.40625 0.421875 0.734375l-0.953125 0.421875q-0.0625 -0.265625 -0.265625 -0.53125q-0.203125 -0.265625 -0.546875 -0.453125q-0.34375 -0.1875 -0.859375 -0.1875q-0.421875 0 -0.796875 0.171875q-0.359375 0.171875 -0.578125 0.46875q-0.21875 0.28125 -0.21875 0.671875q0 0.53125 0.421875 0.84375q0.4375 0.3125 1.171875 0.5625l0.5625 0.203125q0.390625 0.125 0.78125 0.328125q0.40625 0.1875 0.75 0.484375q0.359375 0.296875 0.5625 0.734375q0.21875 0.4375 0.21875 1.046875q0 0.671875 -0.265625 1.1406097q-0.25 0.46875 -0.671875 0.78125q-0.40625 0.296875 -0.90625 0.421875q-0.5 0.140625 -0.984375 0.140625zm4.337799 -0.1875l0 -8.593735l2.59375 0q1.34375 0 2.3125 0.546875q0.984375 0.546875 1.5 1.515625q0.53125 0.96875 0.53125 2.234375q0 1.265625 -0.53125 2.234375q-0.515625 0.95310974 -1.5 1.5156097q-0.96875 0.546875 -2.3125 0.546875l-2.59375 0zm1.03125 -0.96875l1.5625 0q1.015625 0 1.75 -0.39060974q0.75 -0.390625 1.15625 -1.125q0.40625 -0.75 0.40625 -1.8125q0 -1.0625 -0.40625 -1.796875q-0.40625 -0.75 -1.15625 -1.140625q-0.734375 -0.390625 -1.75 -0.390625l-1.5625 0l0 6.6562347zm7.4048157 0.96875l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.75 -0.375 1.328125q-0.359375 0.5625 -0.96875 0.890625q-0.609375 0.328125 -1.328125 0.328125l-2.359375 0l0 -0.96875l2.375 0q0.5 0 0.859375 -0.234375q0.359375 -0.234375 0.5625 -0.59375q0.203125 -0.359375 0.203125 -0.75q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m398.381 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.947144 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390564 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m398.381 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.947144 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390564 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m405.1216 257.1586l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.453125 -0.171875 0.90625q-0.171875 0.453125 -0.53125 0.828125q-0.34375 0.359375 -0.890625 0.59375q-0.546875 0.21875 -1.296875 0.21875l-2.140625 0l0 -0.953125l2.296875 0q0.453125 0 0.828125 -0.203125q0.390625 -0.203125 0.625 -0.5625q0.25 -0.359375 0.25 -0.828125q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0zm2.078125 -3.9374847l1.125 -0.0625l2.703125 3.9531097l0 0.046875l-1.203125 0l-2.625 -3.9374847zm4.9539185 3.9374847l0 -8.593735l4.96875 0l0 0.96875l-3.9375 0l0 7.6249847l-1.03125 0zm0.546875 -3.7187347l0 -0.96875l4.046875 0l0 0.96875l-4.046875 0zm9.73642 3.9062347q-0.9375 0 -1.75 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.59375 -0.60935974 -0.9375 -1.4218597q-0.328125 -0.828125 -0.328125 -1.78125q0 -0.953125 0.328125 -1.765625q0.34375 -0.828125 0.9375 -1.421875q0.609375 -0.609375 1.421875 -0.953125q0.8125 -0.34375 1.75 -0.34375q0.671875 0 1.234375 0.171875q0.578125 0.15625 1.046875 0.46875q0.484375 0.3125 0.875 0.765625l-0.734375 0.703125q-0.328125 -0.390625 -0.703125 -0.640625q-0.359375 -0.265625 -0.78125 -0.375q-0.421875 -0.125 -0.9375 -0.125q-0.921875 0 -1.703125 0.4375q-0.765625 0.421875 -1.234375 1.21875q-0.46875 0.78125 -0.46875 1.859375q0 1.0625 0.46875 1.859375q0.46875 0.796875 1.234375 1.234375q0.78125 0.42185974 1.703125 0.42185974q0.578125 0 1.046875 -0.15625q0.46875 -0.15625 0.875 -0.43748474q0.40625 -0.296875 0.734375 -0.6875l0.75 0.71875q-0.375 0.43748474 -0.90625 0.79685974q-0.515625 0.34375 -1.140625 0.546875q-0.625 0.1875 -1.359375 0.1875zm7.2457886 0q-0.921875 0 -1.640625 -0.421875q-0.703125 -0.4375 -1.109375 -1.1718597q-0.390625 -0.734375 -0.390625 -1.65625q0 -0.90625 0.390625 -1.640625q0.40625 -0.75 1.109375 -1.171875q0.71875 -0.4375 1.640625 -0.4375q0.921875 0 1.625 0.4375q0.71875 0.4375 1.109375 1.1875q0.40625 0.734375 0.40625 1.625q0 0.921875 -0.40625 1.65625q-0.390625 0.73435974 -1.109375 1.1718597q-0.703125 0.421875 -1.625 0.421875zm0 -0.921875q0.546875 0 1.03125 -0.265625q0.484375 -0.28123474 0.78125 -0.79685974q0.3125 -0.53125 0.3125 -1.265625q0 -0.734375 -0.3125 -1.25q-0.296875 -0.53125 -0.78125 -0.796875q-0.484375 -0.28125 -1.03125 -0.28125q-0.546875 0 -1.046875 0.28125q-0.484375 0.265625 -0.796875 0.796875q-0.296875 0.515625 -0.296875 1.25q0 0.734375 0.296875 1.265625q0.3125 0.515625 0.796875 0.79685974q0.5 0.265625 1.046875 0.265625zm4.203949 0.734375l0 -6.1249847l0.984375 0l0 0.90625l0.046875 0q0.15625 -0.296875 0.453125 -0.546875q0.296875 -0.25 0.671875 -0.390625q0.375 -0.15625 0.78125 -0.15625q0.6875 0 1.171875 0.328125q0.5 0.328125 0.703125 0.859375q0.3125 -0.515625 0.84375 -0.84375q0.53125 -0.34375 1.265625 -0.34375q1.09375 0 1.609375 0.671875q0.515625 0.65625 0.515625 1.71875l0 3.9218597l-1.015625 0l0 -3.7656097q0 -0.890625 -0.359375 -1.25q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.84375 0.28125q-0.359375 0.265625 -0.578125 0.703125q-0.203125 0.4375 -0.203125 0.953125l0 3.4531097l-1.015625 0l0 -3.7499847q0 -0.890625 -0.359375 -1.265625q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.828125 0.28125q-0.359375 0.265625 -0.578125 0.71875q-0.203125 0.4375 -0.203125 0.953125l0 3.4374847l-1.03125 0zm10.500061 0l0 -6.1249847l0.984375 0l0 0.90625l0.046875 0q0.15625 -0.296875 0.453125 -0.546875q0.296875 -0.25 0.671875 -0.390625q0.375 -0.15625 0.78125 -0.15625q0.6875 0 1.171875 0.328125q0.5 0.328125 0.703125 0.859375q0.3125 -0.515625 0.84375 -0.84375q0.53125 -0.34375 1.265625 -0.34375q1.09375 0 1.609375 0.671875q0.515625 0.65625 0.515625 1.71875l0 3.9218597l-1.015625 0l0 -3.7656097q0 -0.890625 -0.359375 -1.25q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.84375 0.28125q-0.359375 0.265625 -0.578125 0.703125q-0.203125 0.4375 -0.203125 0.953125l0 3.4531097l-1.015625 0l0 -3.7499847q0 -0.890625 -0.359375 -1.265625q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.828125 0.28125q-0.359375 0.265625 -0.578125 0.71875q-0.203125 0.4375 -0.203125 0.953125l0 3.4374847l-1.03125 0z" fill-rule="nonzero"/><g filter="url(#shadowFilter-p.6)"><use xlink:href="#p.6" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.6" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.6"><path fill="#f6b26b" d="m1.2233764 468.88928l0 0c0 -5.2562256 4.2610183 -9.517242 9.517251 -9.517242l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729721 2.7875366c1.7848206 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.261017 9.517242 -9.517258 9.517242l-235.35919 0c-5.2562327 0 -9.517251 -4.2609863 -9.517251 -9.517242z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m1.2233764 468.88928l0 0c0 -5.2562256 4.2610183 -9.517242 9.517251 -9.517242l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729721 2.7875366c1.7848206 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.261017 9.517242 -9.517258 9.517242l-235.35919 0c-5.2562327 0 -9.517251 -4.2609863 -9.517251 -9.517242z" fill-rule="evenodd"/><path fill="#000000" d="m99.084404 494.0332q-1.265625 0 -2.34375 -0.453125q-1.078125 -0.453125 -1.890625 -1.25q-0.8125 -0.8125 -1.265625 -1.90625q-0.4375 -1.109375 -0.4375 -2.375q0 -1.28125 0.4375 -2.359375q0.453125 -1.09375 1.265625 -1.890625q0.8125 -0.8125 1.890625 -1.265625q1.078125 -0.46875 2.34375 -0.46875q0.875 0 1.640625 0.234375q0.765625 0.21875 1.40625 0.640625q0.640625 0.40625 1.140625 1.0l-0.96875 0.953125q-0.4375 -0.53125 -0.921875 -0.859375q-0.484375 -0.34375 -1.0625 -0.5q-0.5625 -0.171875 -1.234375 -0.171875q-1.234375 0 -2.28125 0.578125q-1.03125 0.578125 -1.65625 1.625q-0.625 1.046875 -0.625 2.484375q0 1.4375 0.625 2.5q0.625 1.046875 1.65625 1.625q1.046875 0.5625 2.28125 0.5625q0.75 0 1.375 -0.203125q0.640625 -0.203125 1.171875 -0.578125q0.546875 -0.390625 0.984375 -0.921875l1.0 0.953125q-0.5 0.59375 -1.203125 1.0625q-0.6875 0.46875 -1.53125 0.734375q-0.84375 0.25 -1.796875 0.25zm9.645386 0q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.6260223 0.96875l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.013519 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm4.980629 2.140625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm9.27002 0.25q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.706024 0.96875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.376007 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm6.9210052 0.25q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.383011 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.7)"><use xlink:href="#p.7" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.7" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.7"><path fill="#f6b26b" d="m4.684006 544.50085l0 0c0 -5.2562866 4.2610183 -9.517273 9.51725 -9.517273l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729706 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2610016 9.517273 -9.517242 9.517273l-235.35919 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.684006 544.50085l0 0c0 -5.2562866 4.2610183 -9.517273 9.51725 -9.517273l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729706 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2610016 9.517273 -9.517242 9.517273l-235.35919 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path fill="#000000" d="m112.98605 569.3948l0 -11.453125l3.875 0q0.953125 0 1.765625 0.4375q0.8125 0.421875 1.296875 1.1875q0.484375 0.75 0.484375 1.765625q0 0.609375 -0.234375 1.21875q-0.21875 0.59375 -0.6875 1.09375q-0.46875 0.484375 -1.203125 0.78125q-0.71875 0.296875 -1.734375 0.296875l-2.84375 0l0 -1.265625l3.078125 0q0.578125 0 1.09375 -0.265625q0.515625 -0.28125 0.828125 -0.75q0.328125 -0.484375 0.328125 -1.109375q0 -0.515625 -0.265625 -0.984375q-0.265625 -0.484375 -0.75 -0.796875q-0.46875 -0.3125 -1.140625 -0.3125l-2.53125 0l0 10.15625l-1.359375 0zm2.765625 -5.25l1.484375 -0.078125l3.625 5.265625l0 0.0625l-1.609375 0l-3.5 -5.25zm8.794525 5.5q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm9.150391 1.1875q-1.109375 0 -2.0 -0.546875q-0.890625 -0.5625 -1.40625 -1.53125q-0.5 -0.984375 -0.5 -2.25q0 -1.265625 0.5 -2.234375q0.515625 -0.984375 1.40625 -1.546875q0.890625 -0.5625 2.0 -0.5625q0.65625 0 1.1875 0.21875q0.546875 0.203125 0.953125 0.546875q0.421875 0.328125 0.640625 0.71875l0.0625 0l-0.0625 -1.140625l0 -3.375l1.359375 0l0 11.453125l-1.296875 0l0 -1.203125l-0.0625 0q-0.21875 0.375 -0.640625 0.71875q-0.40625 0.328125 -0.953125 0.53125q-0.53125 0.203125 -1.1875 0.203125zm0.140625 -1.21875q0.703125 0 1.3125 -0.375q0.625 -0.390625 1.0 -1.078125q0.390625 -0.703125 0.390625 -1.65625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -1.0 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.703125 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.65625q0 0.9375 0.375 1.640625q0.390625 0.703125 1.0 1.09375q0.625 0.375 1.328125 0.375zm6.147644 0.96875l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm6.4891357 9.90625q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.8)"><use xlink:href="#p.8" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.8" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.8"><path fill="#1155cc" d="m274.31787 468.88928l0 0c0 -5.2562256 4.261017 -9.517242 9.517242 -9.517242l234.94974 0c2.52417 0 4.9448853 1.0026855 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.2610474 9.517242 -9.517273 9.517242l-234.94974 0c-5.2562256 0 -9.517242 -4.2609863 -9.517242 -9.517242z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m274.31787 468.88928l0 0c0 -5.2562256 4.261017 -9.517242 9.517242 -9.517242l234.94974 0c2.52417 0 4.9448853 1.0026855 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.2610474 9.517242 -9.517273 9.517242l-234.94974 0c-5.2562256 0 -9.517242 -4.2609863 -9.517242 -9.517242z" fill-rule="evenodd"/><path fill="#000000" d="m344.86313 493.7832l-4.0625 -11.453125l1.515625 0l2.828125 8.296875l0.375 1.140625l0.078125 0l0.390625 -1.140625l2.96875 -8.296875l1.5 0l-4.1875 11.453125l-1.40625 0zm6.635376 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.296875l0.078125 0q0.203125 -0.5 0.578125 -0.828125q0.375 -0.34375 0.875 -0.515625q0.515625 -0.1875 1.03125 -0.1875q0.234375 0 0.34375 0.015625q0.125 0 0.234375 0.03125l0 1.40625q-0.15625 -0.03125 -0.34375 -0.046875q-0.171875 -0.03125 -0.390625 -0.03125q-0.6875 0 -1.21875 0.3125q-0.53125 0.3125 -0.828125 0.875q-0.28125 0.5625 -0.28125 1.296875l0 4.53125l-1.359375 0zm5.5 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm7.9512634 2.390625q-1.5 0 -2.265625 -0.875q-0.765625 -0.875 -0.765625 -2.40625l0 -5.125l1.359375 0l0 4.921875q0 1.21875 0.5625 1.75q0.5625 0.515625 1.390625 0.515625q0.71875 0 1.25 -0.375q0.53125 -0.390625 0.8125 -0.96875q0.296875 -0.59375 0.296875 -1.234375l0 -4.609375l1.359375 0l0 8.15625l-1.296875 0l0 -1.1875l-0.0625 0q-0.203125 0.390625 -0.625 0.71875q-0.40625 0.328125 -0.921875 0.515625q-0.515625 0.203125 -1.09375 0.203125zm8.536652 0q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.761627 0.9375l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm12.40863 0.25q-1.265625 0 -2.34375 -0.453125q-1.078125 -0.453125 -1.890625 -1.25q-0.8125 -0.8125 -1.265625 -1.90625q-0.4375 -1.109375 -0.4375 -2.375q0 -1.28125 0.4375 -2.359375q0.453125 -1.09375 1.265625 -1.890625q0.8125 -0.8125 1.890625 -1.265625q1.078125 -0.46875 2.34375 -0.46875q0.875 0 1.640625 0.234375q0.765625 0.21875 1.40625 0.640625q0.640625 0.40625 1.140625 1.0l-0.96875 0.953125q-0.4375 -0.53125 -0.921875 -0.859375q-0.484375 -0.34375 -1.0625 -0.5q-0.5625 -0.171875 -1.234375 -0.171875q-1.234375 0 -2.28125 0.578125q-1.03125 0.578125 -1.65625 1.625q-0.625 1.046875 -0.625 2.484375q0 1.4375 0.625 2.5q0.625 1.046875 1.65625 1.625q1.046875 0.5625 2.28125 0.5625q0.75 0 1.375 -0.203125q0.640625 -0.203125 1.171875 -0.578125q0.546875 -0.390625 0.984375 -0.921875l1.0 0.953125q-0.5 0.59375 -1.203125 1.0625q-0.6875 0.46875 -1.53125 0.734375q-0.84375 0.25 -1.796875 0.25zm9.645386 0q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.6260376 0.96875l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.013519 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm4.9806213 2.140625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm9.27002 0.25q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.706024 0.96875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.376007 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm6.9210205 0.25q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.3829956 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.9)"><use xlink:href="#p.9" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.9" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.9"><path fill="#1155cc" d="m277.77325 544.50085l0 0c0 -5.2562866 4.261017 -9.517273 9.517242 -9.517273l235.35922 0c2.524109 0 4.9448853 1.0026855 6.7296753 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2609863 9.517273 -9.517212 9.517273l-235.35922 0c-5.2562256 0 -9.517242 -4.2610474 -9.517242 -9.517273z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m277.77325 544.50085l0 0c0 -5.2562866 4.261017 -9.517273 9.517242 -9.517273l235.35922 0c2.524109 0 4.9448853 1.0026855 6.7296753 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2609863 9.517273 -9.517212 9.517273l-235.35922 0c-5.2562256 0 -9.517242 -4.2610474 -9.517242 -9.517273z" fill-rule="evenodd"/><path fill="#000000" d="m348.32324 569.3948l-4.0625 -11.453125l1.515625 0l2.828125 8.296875l0.375 1.140625l0.078125 0l0.390625 -1.140625l2.96875 -8.296875l1.5 0l-4.1875 11.453125l-1.40625 0zm6.635376 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.296875l0.078125 0q0.203125 -0.5 0.578125 -0.828125q0.375 -0.34375 0.875 -0.515625q0.515625 -0.1875 1.03125 -0.1875q0.234375 0 0.34375 0.015625q0.125 0 0.234375 0.03125l0 1.40625q-0.15625 -0.03125 -0.34375 -0.046875q-0.171875 -0.03125 -0.390625 -0.03125q-0.6875 0 -1.21875 0.3125q-0.53125 0.3125 -0.828125 0.875q-0.28125 0.5625 -0.28125 1.296875l0 4.53125l-1.359375 0zm5.5 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm7.9512634 2.390625q-1.5 0 -2.265625 -0.875q-0.765625 -0.875 -0.765625 -2.40625l0 -5.125l1.359375 0l0 4.921875q0 1.21875 0.5625 1.75q0.5625 0.515625 1.390625 0.515625q0.71875 0 1.25 -0.375q0.53125 -0.390625 0.8125 -0.96875q0.296875 -0.59375 0.296875 -1.234375l0 -4.609375l1.359375 0l0 8.15625l-1.296875 0l0 -1.1875l-0.0625 0q-0.203125 0.390625 -0.625 0.71875q-0.40625 0.328125 -0.921875 0.515625q-0.515625 0.203125 -1.09375 0.203125zm8.536652 0q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.761627 0.9375l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm7.3536377 0l0 -11.453125l1.359375 0l0 10.15625l5.0 0l0 1.296875l-6.359375 0zm8.003387 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.976013 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.5 -1.65625l0 -1.75l4.6875 -4.75l1.734375 0l0 0.0625l-6.421875 6.4375zm4.890625 1.65625l-2.953125 -4.390625l0.96875 -0.9375l3.609375 5.265625l0 0.0625l-1.625 0zm6.6670227 0l0 -11.453125l1.359375 0l0 10.15625l5.0 0l0 1.296875l-6.359375 0zm10.472137 0.25q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm7.9297485 3.984375q-0.046875 0.109375 -0.109375 0.234375q-0.0625 0.140625 -0.078125 0.171875l-1.40625 0q0.09375 -0.203125 0.203125 -0.453125q0.109375 -0.25 0.265625 -0.59375q0.09375 -0.21875 0.171875 -0.390625q0.09375 -0.171875 0.1875 -0.390625q0.109375 -0.21875 0.25 -0.53125l1.125 -2.4375l0.296875 -0.75l2.46875 -6.0625l1.46875 0l-4.21875 9.734375q-0.0625 0.15625 -0.203125 0.46875q-0.140625 0.3125 -0.265625 0.59375q-0.109375 0.296875 -0.15625 0.40625zm0.296875 -3.390625l-3.453125 -7.8125l1.46875 0l2.5625 6.0625l0.140625 0l-0.71875 1.75zm9.048004 0.59375q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.383026 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0z" fill-rule="nonzero"/></g><path fill="#a2c4c9" d="m462.61346 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.947144 0c2.333374 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m462.61346 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.947144 0c2.333374 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m490.619 257.23672q-0.3125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.3125 0.21875 -0.53123474q0.234375 -0.21875 0.546875 -0.21875q0.328125 0 0.546875 0.21875q0.234375 0.21873474 0.234375 0.53123474q0 0.328125 -0.234375 0.5625q-0.21875 0.21875 -0.546875 0.21875zm2.765625 0q-0.3125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.3125 0.21875 -0.53123474q0.234375 -0.21875 0.546875 -0.21875q0.328125 0 0.546875 0.21875q0.234375 0.21873474 0.234375 0.53123474q0 0.328125 -0.234375 0.5625q-0.21875 0.21875 -0.546875 0.21875zm2.75 0q-0.3125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.3125 0.21875 -0.53123474q0.234375 -0.21875 0.546875 -0.21875q0.328125 0 0.546875 0.21875q0.234375 0.21873474 0.234375 0.53123474q0 0.328125 -0.234375 0.5625q-0.21875 0.21875 -0.546875 0.21875z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m446.68536 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m446.68536 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m480.01547 145.56639q-0.4375 0 -0.734375 -0.296875q-0.296875 -0.296875 -0.296875 -0.71875q0 -0.421875 0.296875 -0.71875q0.296875 -0.296875 0.734375 -0.296875q0.4375 0 0.71875 0.296875q0.296875 0.296875 0.296875 0.71875q0 0.421875 -0.296875 0.71875q-0.28125 0.296875 -0.71875 0.296875zm3.6875 0q-0.4375 0 -0.734375 -0.296875q-0.296875 -0.296875 -0.296875 -0.71875q0 -0.421875 0.296875 -0.71875q0.296875 -0.296875 0.734375 -0.296875q0.4375 0 0.71875 0.296875q0.296875 0.296875 0.296875 0.71875q0 0.421875 -0.296875 0.71875q-0.28125 0.296875 -0.71875 0.296875zm3.671875 0q-0.4375 0 -0.734375 -0.296875q-0.296875 -0.296875 -0.296875 -0.71875q0 -0.421875 0.296875 -0.71875q0.296875 -0.296875 0.734375 -0.296875q0.4375 0 0.71875 0.296875q0.296875 0.296875 0.296875 0.71875q0 0.421875 -0.296875 0.71875q-0.28125 0.296875 -0.71875 0.296875z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m268.4268 284.34842l0 23.811005" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m268.4268 284.34842l0 23.811005" fill-rule="evenodd"/><g filter="url(#shadowFilter-p.10)"><use xlink:href="#p.10" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.10" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.10"><path fill="#3c78d8" d="m4.8992295 393.27774l0 0c0 -5.256256 4.2610183 -9.517273 9.51725 -9.517273l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848511 2.7875366 4.205597 2.7875366 6.7297363l0 38.06784c0 5.2562256 -4.2609863 9.517273 -9.517273 9.517273l-508.0206 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 393.27774l0 0c0 -5.256256 4.2610183 -9.517273 9.51725 -9.517273l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848511 2.7875366 4.205597 2.7875366 6.7297363l0 38.06784c0 5.2562256 -4.2609863 9.517273 -9.517273 9.517273l-508.0206 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path fill="#000000" d="m220.21194 418.17166l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm9.133026 0.25q-1.265625 0 -2.34375 -0.453125q-1.078125 -0.453125 -1.890625 -1.25q-0.8125 -0.8125 -1.265625 -1.90625q-0.4375 -1.109375 -0.4375 -2.375q0 -1.28125 0.4375 -2.359375q0.453125 -1.09375 1.265625 -1.890625q0.8125 -0.8125 1.890625 -1.265625q1.078125 -0.46875 2.34375 -0.46875q0.875 0 1.640625 0.234375q0.765625 0.21875 1.40625 0.640625q0.640625 0.40625 1.140625 1.0l-0.96875 0.953125q-0.4375 -0.53125 -0.921875 -0.859375q-0.484375 -0.34375 -1.0625 -0.5q-0.5625 -0.171875 -1.234375 -0.171875q-1.234375 0 -2.28125 0.578125q-1.03125 0.578125 -1.65625 1.625q-0.625 1.046875 -0.625 2.484375q0 1.4375 0.625 2.5q0.625 1.046875 1.65625 1.625q1.046875 0.5625 2.28125 0.5625q0.75 0 1.375 -0.203125q0.640625 -0.203125 1.171875 -0.578125q0.546875 -0.390625 0.984375 -0.921875l1.0 0.953125q-0.5 0.59375 -1.203125 1.0625q-0.6875 0.46875 -1.53125 0.734375q-0.84375 0.25 -1.796875 0.25zm6.481018 -0.25l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm9.33577 0l0 -10.640625l1.359375 0l0 10.640625l-1.359375 0zm-3.203125 -10.15625l0 -1.296875l7.7656097 0l0 1.296875l-7.7656097 0zm8.241745 10.15625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm8.273773 0.25q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.7616577 0.9375l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm11.739746 0.25q-0.921875 0 -1.625 -0.296875q-0.6875 -0.296875 -1.140625 -0.796875q-0.453125 -0.5 -0.671875 -1.09375l1.203125 -0.546875q0.328125 0.734375 0.9375 1.140625q0.609375 0.40625 1.390625 0.40625q0.75 0 1.25 -0.296875q0.515625 -0.3125 0.515625 -0.90625q0 -0.375 -0.21875 -0.625q-0.203125 -0.25 -0.609375 -0.421875q-0.390625 -0.171875 -0.96875 -0.3125l-1.0 -0.265625q-0.5625 -0.15625 -1.078125 -0.4375q-0.515625 -0.296875 -0.828125 -0.75q-0.3125 -0.453125 -0.3125 -1.109375q0 -0.734375 0.421875 -1.265625q0.4375 -0.53125 1.140625 -0.8125q0.703125 -0.28125 1.515625 -0.28125q0.703125 0 1.3125 0.203125q0.625 0.203125 1.078125 0.59375q0.46875 0.390625 0.703125 0.96875l-1.171875 0.546875q-0.3125 -0.609375 -0.828125 -0.84375q-0.5 -0.25 -1.125 -0.25q-0.671875 0 -1.171875 0.296875q-0.5 0.296875 -0.5 0.8125q0 0.515625 0.40625 0.765625q0.40625 0.25 1.0 0.421875l1.1875 0.296875q1.203125 0.3125 1.8125 0.90625q0.609375 0.59375 0.609375 1.46875q0 0.765625 -0.4375 1.328125q-0.4375 0.546875 -1.171875 0.859375q-0.734375 0.296875 -1.625 0.296875zm4.6442566 3.203125l0 -11.609375l1.28125 0l0 1.21875l0.078125 0q0.21875 -0.390625 0.625 -0.71875q0.40625 -0.34375 0.953125 -0.546875q0.546875 -0.21875 1.203125 -0.21875q1.109375 0 1.984375 0.5625q0.890625 0.5625 1.40625 1.546875q0.515625 0.96875 0.515625 2.234375q0 1.265625 -0.515625 2.25q-0.515625 0.96875 -1.40625 1.53125q-0.875 0.546875 -1.984375 0.546875q-0.984375 0 -1.71875 -0.4375q-0.734375 -0.453125 -1.0625 -1.015625l-0.078125 0l0.078125 1.125l0 3.53125l-1.359375 0zm4.0 -4.421875q0.703125 0 1.3125 -0.375q0.609375 -0.390625 0.984375 -1.09375q0.390625 -0.703125 0.390625 -1.640625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -0.984375 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.71875 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.390625 0.6875 -0.390625 1.65625q0 0.953125 0.390625 1.65625q0.390625 0.6875 1.0 1.078125q0.609375 0.375 1.328125 0.375zm9.382019 1.21875q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.626007 0.96875l0 -8.15625l1.28125 0l0 1.296875l0.078125 0q0.203125 -0.5 0.578125 -0.828125q0.375 -0.34375 0.875 -0.515625q0.515625 -0.1875 1.03125 -0.1875q0.234375 0 0.34375 0.015625q0.125 0 0.234375 0.03125l0 1.40625q-0.15625 -0.03125 -0.34375 -0.046875q-0.171875 -0.03125 -0.390625 -0.03125q-0.6875 0 -1.21875 0.3125q-0.53125 0.3125 -0.828125 0.875q-0.28125 0.5625 -0.28125 1.296875l0 4.53125l-1.359375 0zm5.5 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875z" fill-rule="nonzero"/></g><path fill="#000000" fill-opacity="0.0" d="m268.4268 365.25128l0 18.519684" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m268.4268 365.25128l0 18.519684" fill-rule="evenodd"/><path fill="#a4c2f4" d="m205.13545 82.27259l314.42523 0c3.2869873 0 6.439392 1.3057632 8.763672 3.6300278c2.3242188 2.3242722 3.630005 5.4766617 3.630005 8.763672l0 12.3937c0 0.0015640259 -0.0012817383 0.0028305054 -0.0028076172 0.0028305054l-339.20978 -0.0028305054l0 0c-0.0015563965 0 -0.002822876 -0.0012664795 -0.002822876 -0.002822876l0.002822876 -12.390877l0 0c0 -6.8448486 5.5488586 -12.3937 12.393707 -12.3937z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m205.13545 82.27259l314.42523 0c3.2869873 0 6.439392 1.3057632 8.763672 3.6300278c2.3242188 2.3242722 3.630005 5.4766617 3.630005 8.763672l0 12.3937c0 0.0015640259 -0.0012817383 0.0028305054 -0.0028076172 0.0028305054l-339.20978 -0.0028305054l0 0c-0.0015563965 0 -0.002822876 -0.0012664795 -0.002822876 -0.002822876l0.002822876 -12.390877l0 0c0 -6.8448486 5.5488586 -12.3937 12.393707 -12.3937z" fill-rule="evenodd"/><path fill="#000000" d="m339.0376 99.98258l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.213104 5.5l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.619873 3.796875l0 -6.59375l-1.140625 0l0 -1.0l1.140625 0l0 -0.8125q0 -0.765625 0.125 -1.140625q0.1875 -0.5 0.65625 -0.8125q0.46875 -0.3125 1.3125 -0.3125q0.546875 0 1.203125 0.125l-0.1875 1.125q-0.40625 -0.0625 -0.765625 -0.0625q-0.578125 0 -0.828125 0.25q-0.234375 0.25 -0.234375 0.9375l0 0.703125l1.46875 0l0 1.0l-1.46875 0l0 6.59375l-1.28125 0zm3.7594604 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm3.2249146 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.490509 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm6.666748 2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/><path fill="#6d9eeb" d="m17.29293 195.78148l502.26773 0c3.2869873 0 6.439392 1.3057556 8.763672 3.6300201c2.3242188 2.3242798 3.630005 5.4766693 3.630005 8.763672l0 12.393707c0 0.0024261475 -0.001953125 0.0043945312 -0.0043945312 0.0043945312l-527.0507 -0.0043945312l0 0c-0.0024256706 0 -0.004392147 -0.0019683838 -0.004392147 -0.0043945312l0.004392147 -12.389313l0 0c0 -6.8448486 5.5488486 -12.393692 12.393702 -12.393692z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m17.29293 195.78148l502.26773 0c3.2869873 0 6.439392 1.3057556 8.763672 3.6300201c2.3242188 2.3242798 3.630005 5.4766693 3.630005 8.763672l0 12.393707c0 0.0024261475 -0.001953125 0.0043945312 -0.0043945312 0.0043945312l-527.0507 -0.0043945312l0 0c-0.0024256706 0 -0.004392147 -0.0019683838 -0.004392147 -0.0043945312l0.004392147 -12.389313l0 0c0 -6.8448486 5.5488486 -12.393692 12.393702 -12.393692z" fill-rule="evenodd"/><path fill="#000000" d="m239.0048 213.49147l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.213104 5.5l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.3906097 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.5937347 0 -2.5781097 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.6093597 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96873474 0 -1.6093597 0.71875q-0.625 0.71875 -0.625 2.1875zm10.119858 2.640625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm0.77508545 -2.640625q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm12.260498 1.015625l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm1.890625 -1.015625q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.2760925 3.796875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm2.771759 -2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/></g></svg>
\ No newline at end of file +<svg version="1.1" viewBox="0.0 0.0 539.0209973753281 600.7742782152231" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l539.021 0l0 600.7743l-539.021 0l0 -600.7743z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l539.021 0l0 600.7743l-539.021 0z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m362.34805 205.27522l0 -159.87402" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m362.34805 205.27522l0 -159.87402" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m94.91498 205.27522l0 -159.87402" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m94.91498 205.27522l0 -159.87402" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m131.88086 534.9836l0 -116.81891" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m131.88086 534.9836l0 -116.81891" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m404.9701 534.9836l0 -124.62991" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m404.9701 534.9836l0 -124.62991" fill-rule="evenodd"/><g filter="url(#shadowFilter-p.1)"><use xlink:href="#p.1" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.1" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.1"><path fill="#a4c2f4" d="m4.8992295 210.33295l0 0c0 -8.175705 6.627728 -14.803436 14.803446 -14.803436l497.4482 0c3.9261475 0 7.6914673 1.5596466 10.467651 4.3358307c2.776184 2.776184 4.3358154 6.5414886 4.3358154 10.467606l0 59.212006c0 8.17572 -6.6277466 14.803467 -14.803467 14.803467l-497.4482 0c-8.175717 0 -14.803446 -6.6277466 -14.803446 -14.803467z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 210.33295l0 0c0 -8.175705 6.627728 -14.803436 14.803446 -14.803436l497.4482 0c3.9261475 0 7.6914673 1.5596466 10.467651 4.3358307c2.776184 2.776184 4.3358154 6.5414886 4.3358154 10.467606l0 59.212006c0 8.17572 -6.6277466 14.803467 -14.803467 14.803467l-497.4482 0c-8.175717 0 -14.803446 -6.6277466 -14.803446 -14.803467z" fill-rule="evenodd"/></g><g filter="url(#shadowFilter-p.2)"><use xlink:href="#p.2" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.2" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.2"><path fill="#b6d7a8" d="m4.8992295 13.393214l0 0c0 -5.2562323 4.2610183 -9.517251 9.51725 -9.517251l508.0206 0c2.52417 0 4.9448853 1.0027075 6.7297363 2.787538c1.7848511 1.7848306 2.7875366 4.205581 2.7875366 6.729713l0 38.06786c0 5.256233 -4.2609863 9.51725 -9.517273 9.51725l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.51725z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 13.393214l0 0c0 -5.2562323 4.2610183 -9.517251 9.51725 -9.517251l508.0206 0c2.52417 0 4.9448853 1.0027075 6.7297363 2.787538c1.7848511 1.7848306 2.7875366 4.205581 2.7875366 6.729713l0 38.06786c0 5.256233 -4.2609863 9.51725 -9.517273 9.51725l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.51725z" fill-rule="evenodd"/><path fill="#000000" d="m221.21878 39.34714l5.078125 -13.359375l1.78125 0l5.078125 13.359375l-1.734375 0l-3.671875 -9.984375l-0.53125 -1.4375l-0.0625 0l-0.53125 1.4375l-3.671875 9.984375l-1.734375 0zm5.78125 -3.671875l0 -1.5l3.078125 0l0.546875 1.5l-3.625 0zm-3.625 0l0.546875 -1.5l3.078125 0l0 1.5l-3.625 0zm11.327408 7.703125l0 -13.546875l1.515625 0l0 1.421875l0.078125 0q0.25 -0.453125 0.71875 -0.84375q0.484375 -0.390625 1.125 -0.625q0.640625 -0.25 1.390625 -0.25q1.3125 0 2.328125 0.65625q1.03125 0.65625 1.625 1.796875q0.609375 1.125 0.609375 2.609375q0 1.46875 -0.609375 2.609375q-0.59375 1.140625 -1.625 1.796875q-1.015625 0.640625 -2.328125 0.640625q-1.125 0 -1.984375 -0.515625q-0.859375 -0.53125 -1.25 -1.1875l-0.078125 0l0.078125 1.3125l0 4.125l-1.59375 0zm4.671875 -5.171875q0.8125 0 1.53125 -0.4375q0.71875 -0.4375 1.15625 -1.25q0.4375 -0.828125 0.4375 -1.921875q0 -1.125 -0.4375 -1.9375q-0.4375 -0.8125 -1.15625 -1.25q-0.71875 -0.4375 -1.53125 -0.4375q-0.828125 0 -1.546875 0.4375q-0.71875 0.4375 -1.171875 1.25q-0.4375 0.8125 -0.4375 1.9375q0 1.109375 0.4375 1.921875q0.453125 0.8125 1.171875 1.25q0.71875 0.4375 1.546875 0.4375zm6.372528 5.171875l0 -13.546875l1.515625 0l0 1.421875l0.078125 0q0.25 -0.453125 0.71875 -0.84375q0.484375 -0.390625 1.125 -0.625q0.640625 -0.25 1.390625 -0.25q1.3125 0 2.328125 0.65625q1.03125 0.65625 1.625 1.796875q0.609375 1.125 0.609375 2.609375q0 1.46875 -0.609375 2.609375q-0.59375 1.140625 -1.625 1.796875q-1.015625 0.640625 -2.328125 0.640625q-1.125 0 -1.984375 -0.515625q-0.859375 -0.53125 -1.25 -1.1875l-0.078125 0l0.078125 1.3125l0 4.125l-1.59375 0zm4.671875 -5.171875q0.8125 0 1.53125 -0.4375q0.71875 -0.4375 1.15625 -1.25q0.4375 -0.828125 0.4375 -1.921875q0 -1.125 -0.4375 -1.9375q-0.4375 -0.8125 -1.15625 -1.25q-0.71875 -0.4375 -1.53125 -0.4375q-0.828125 0 -1.546875 0.4375q-0.71875 0.4375 -1.171875 1.25q-0.4375 0.8125 -0.4375 1.9375q0 1.109375 0.4375 1.921875q0.453125 0.8125 1.171875 1.25q0.71875 0.4375 1.546875 0.4375zm6.372513 1.140625l0 -13.359375l1.59375 0l0 13.359375l-1.59375 0zm4.0927124 0l0 -9.515625l1.578125 0l0 9.515625l-1.578125 0zm0.78125 -11.265625q-0.46875 0 -0.8125 -0.328125q-0.328125 -0.34375 -0.328125 -0.8125q0 -0.484375 0.328125 -0.8125q0.34375 -0.328125 0.8125 -0.328125q0.484375 0 0.8125 0.328125q0.328125 0.328125 0.328125 0.8125q0 0.46875 -0.328125 0.8125q-0.328125 0.328125 -0.8125 0.328125zm7.497986 11.5625q-1.40625 0 -2.5 -0.65625q-1.078125 -0.671875 -1.703125 -1.8125q-0.609375 -1.15625 -0.609375 -2.578125q0 -1.46875 0.609375 -2.59375q0.625 -1.140625 1.703125 -1.796875q1.09375 -0.671875 2.5 -0.671875q1.609375 0 2.640625 0.734375q1.03125 0.734375 1.46875 1.890625l-1.4375 0.609375q-0.359375 -0.890625 -1.0625 -1.34375q-0.6875 -0.453125 -1.6875 -0.453125q-0.828125 0 -1.546875 0.453125q-0.71875 0.4375 -1.171875 1.25q-0.453125 0.8125 -0.453125 1.921875q0 1.078125 0.453125 1.90625q0.453125 0.8125 1.171875 1.265625q0.71875 0.4375 1.546875 0.4375q1.015625 0 1.734375 -0.46875q0.734375 -0.46875 1.09375 -1.3125l1.40625 0.59375q-0.46875 1.09375 -1.515625 1.859375q-1.03125 0.765625 -2.640625 0.765625zm8.827332 0q-1.0625 0 -1.875 -0.40625q-0.796875 -0.40625 -1.25 -1.125q-0.453125 -0.71875 -0.453125 -1.640625q0 -1.046875 0.53125 -1.765625q0.546875 -0.71875 1.453125 -1.078125q0.921875 -0.359375 2.015625 -0.359375q0.640625 0 1.171875 0.109375q0.546875 0.09375 0.9375 0.234375q0.40625 0.140625 0.625 0.265625l0 -0.578125q0 -1.078125 -0.765625 -1.703125q-0.765625 -0.640625 -1.875 -0.640625q-0.78125 0 -1.46875 0.34375q-0.671875 0.34375 -1.0625 0.953125l-1.203125 -0.890625q0.375 -0.5625 0.9375 -0.96875q0.5625 -0.40625 1.28125 -0.625q0.71875 -0.234375 1.515625 -0.234375q1.9375 0 3.03125 1.03125q1.109375 1.015625 1.109375 2.75l0 6.03125l-1.5 0l0 -1.359375l-0.078125 0q-0.25 0.40625 -0.703125 0.796875q-0.4375 0.375 -1.046875 0.609375q-0.609375 0.25 -1.328125 0.25zm0.140625 -1.390625q0.828125 0 1.5 -0.40625q0.6875 -0.421875 1.09375 -1.109375q0.421875 -0.6875 0.421875 -1.515625q-0.4375 -0.296875 -1.078125 -0.484375q-0.640625 -0.1875 -1.40625 -0.1875q-1.359375 0 -2.0 0.5625q-0.640625 0.5625 -0.640625 1.375q0 0.78125 0.59375 1.28125q0.609375 0.484375 1.515625 0.484375zm5.876068 -8.421875l5.578125 0l0 1.4375l-5.578125 0l0 -1.4375zm1.671875 7.015625l0 -9.703125l1.578125 0l0 9.3125q0 0.75 0.3125 1.15625q0.3125 0.40625 1.015625 0.40625q0.3125 0 0.578125 -0.09375q0.265625 -0.09375 0.46875 -0.21875l0 1.546875q-0.25 0.109375 -0.546875 0.171875q-0.28125 0.078125 -0.765625 0.078125q-1.1875 0 -1.921875 -0.703125q-0.71875 -0.703125 -0.71875 -1.953125zm6.0434875 2.5l0 -9.515625l1.578125 0l0 9.515625l-1.578125 0zm0.78125 -11.265625q-0.46875 0 -0.8125 -0.328125q-0.328125 -0.34375 -0.328125 -0.8125q0 -0.484375 0.328125 -0.8125q0.34375 -0.328125 0.8125 -0.328125q0.484375 0 0.8125 0.328125q0.328125 0.328125 0.328125 0.8125q0 0.46875 -0.328125 0.8125q-0.328125 0.328125 -0.8125 0.328125zm7.576111 11.5625q-1.4375 0 -2.546875 -0.671875q-1.09375 -0.671875 -1.71875 -1.8125q-0.625 -1.15625 -0.625 -2.5625q0 -1.421875 0.625 -2.5625q0.625 -1.15625 1.71875 -1.828125q1.109375 -0.671875 2.546875 -0.671875q1.4375 0 2.53125 0.6875q1.109375 0.671875 1.734375 1.828125q0.625 1.140625 0.625 2.546875q0 1.40625 -0.625 2.5625q-0.625 1.140625 -1.734375 1.8125q-1.09375 0.671875 -2.53125 0.671875zm0 -1.4375q0.859375 0 1.609375 -0.421875q0.75 -0.4375 1.21875 -1.25q0.46875 -0.8125 0.46875 -1.9375q0 -1.140625 -0.46875 -1.953125q-0.46875 -0.8125 -1.21875 -1.234375q-0.75 -0.4375 -1.609375 -0.4375q-0.859375 0 -1.625 0.4375q-0.765625 0.421875 -1.234375 1.234375q-0.46875 0.8125 -0.46875 1.953125q0 1.125 0.46875 1.9375q0.46875 0.8125 1.234375 1.25q0.765625 0.421875 1.625 0.421875zm6.5418396 1.140625l0 -9.515625l1.515625 0l0 1.40625l0.078125 0q0.375 -0.703125 1.21875 -1.203125q0.84375 -0.5 1.859375 -0.5q1.75 0 2.625 1.015625q0.890625 1.015625 0.890625 2.703125l0 6.09375l-1.578125 0l0 -5.859375q0 -1.375 -0.671875 -1.9375q-0.65625 -0.578125 -1.703125 -0.578125q-0.78125 0 -1.375 0.4375q-0.59375 0.4375 -0.9375 1.125q-0.328125 0.6875 -0.328125 1.453125l0 5.359375l-1.59375 0z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.3)"><use xlink:href="#p.3" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.3" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.3"><path fill="#c9daf8" d="m4.8992295 97.076035l0 0c0 -8.175713 6.627728 -14.803444 14.803446 -14.803444l147.81042 0c3.926117 0 7.691437 1.5596466 10.467621 4.3358307c2.776184 2.776184 4.3358307 6.5414963 4.3358307 10.467613l0 59.212006c0 8.17572 -6.6277313 14.803452 -14.803452 14.803452l-147.81042 0c-8.175717 0 -14.803446 -6.6277313 -14.803446 -14.803452z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 97.076035l0 0c0 -8.175713 6.627728 -14.803444 14.803446 -14.803444l147.81042 0c3.926117 0 7.691437 1.5596466 10.467621 4.3358307c2.776184 2.776184 4.3358307 6.5414963 4.3358307 10.467613l0 59.212006c0 8.17572 -6.6277313 14.803452 -14.803452 14.803452l-147.81042 0c-8.175717 0 -14.803446 -6.6277313 -14.803446 -14.803452z" fill-rule="evenodd"/><path fill="#000000" d="m66.37746 133.60204l0 -13.359367l4.015625 0q2.09375 0 3.609375 0.859375q1.515625 0.84375 2.328125 2.359375q0.828125 1.5 0.828125 3.46875q0 1.9531174 -0.828125 3.4531174q-0.8125 1.5 -2.328125 2.359375q-1.515625 0.859375 -3.609375 0.859375l-4.015625 0zm1.578125 -1.515625l2.4375 0q1.578125 0 2.734375 -0.59375q1.15625 -0.609375 1.78125 -1.765625q0.640625 -1.15625 0.640625 -2.7968674q0 -1.65625 -0.640625 -2.8125q-0.625 -1.15625 -1.78125 -1.75q-1.15625 -0.609375 -2.734375 -0.609375l-2.4375 0l0 10.328117zm15.163467 1.8125q-1.375 0 -2.453125 -0.640625q-1.0625 -0.65625 -1.671875 -1.796875q-0.609375 -1.140625 -0.609375 -2.59375q0 -1.3593674 0.5625 -2.5156174q0.578125 -1.15625 1.609375 -1.859375q1.03125 -0.703125 2.4375 -0.703125q1.421875 0 2.4375 0.625q1.015625 0.625 1.5625 1.734375q0.546875 1.09375 0.546875 2.5156174q0 0.125 -0.015625 0.265625q0 0.125 -0.015625 0.21875l-8.1875 0l0 -1.3124924l6.546875 0q-0.015625 -0.390625 -0.1875 -0.84375q-0.15625 -0.46875 -0.5 -0.859375q-0.34375 -0.40625 -0.875 -0.65625q-0.53125 -0.25 -1.3125 -0.25q-0.9375 0 -1.625 0.484375q-0.671875 0.46875 -1.046875 1.296875q-0.359375 0.8125 -0.359375 1.8593674q0 1.203125 0.46875 2.015625q0.46875 0.796875 1.203125 1.1875q0.75 0.390625 1.546875 0.390625q1.046875 0 1.71875 -0.484375q0.6875 -0.5 1.09375 -1.234375l1.34375 0.65625q-0.5625 1.078125 -1.609375 1.796875q-1.03125 0.703125 -2.609375 0.703125zm8.879303 -0.296875l-3.84375 -9.515617l1.703125 0l2.953125 7.6562424l0.03125 0l2.984375 -7.6562424l1.65625 0l-3.875 9.515617l-1.609375 0zm6.9486847 0l0 -9.515617l1.578125 0l0 9.515617l-1.578125 0zm0.78125 -11.265617q-0.46875 0 -0.8125 -0.328125q-0.328125 -0.34375 -0.328125 -0.8125q0 -0.484375 0.328125 -0.8125q0.34375 -0.328125 0.8125 -0.328125q0.484375 0 0.8125 0.328125q0.328125 0.328125 0.328125 0.8125q0 0.46875 -0.328125 0.8125q-0.328125 0.328125 -0.8125 0.328125zm7.4979706 11.562492q-1.40625 0 -2.5 -0.65625q-1.078125 -0.671875 -1.703125 -1.8125q-0.609375 -1.15625 -0.609375 -2.578125q0 -1.4687424 0.609375 -2.5937424q0.625 -1.140625 1.703125 -1.796875q1.09375 -0.671875 2.5 -0.671875q1.609375 0 2.640625 0.734375q1.03125 0.734375 1.46875 1.890625l-1.4375 0.609375q-0.359375 -0.890625 -1.0625 -1.34375q-0.6875 -0.453125 -1.6875 -0.453125q-0.828125 0 -1.546875 0.453125q-0.71875 0.4375 -1.171875 1.25q-0.453125 0.8125 -0.453125 1.9218674q0 1.078125 0.453125 1.90625q0.453125 0.8125 1.171875 1.265625q0.71875 0.4375 1.546875 0.4375q1.015625 0 1.734375 -0.46875q0.734375 -0.46875 1.09375 -1.3125l1.40625 0.59375q-0.46875 1.09375 -1.515625 1.859375q-1.03125 0.765625 -2.640625 0.765625zm10.014847 0q-1.375 0 -2.453125 -0.640625q-1.0625 -0.65625 -1.671875 -1.796875q-0.609375 -1.140625 -0.609375 -2.59375q0 -1.3593674 0.5625 -2.5156174q0.578125 -1.15625 1.609375 -1.859375q1.03125 -0.703125 2.4375 -0.703125q1.421875 0 2.4375 0.625q1.015625 0.625 1.5625 1.734375q0.546875 1.09375 0.546875 2.5156174q0 0.125 -0.015625 0.265625q0 0.125 -0.015625 0.21875l-8.1875 0l0 -1.3124924l6.546875 0q-0.015625 -0.390625 -0.1875 -0.84375q-0.15625 -0.46875 -0.5 -0.859375q-0.34375 -0.40625 -0.875 -0.65625q-0.53125 -0.25 -1.3125 -0.25q-0.9375 0 -1.625 0.484375q-0.671875 0.46875 -1.046875 1.296875q-0.359375 0.8125 -0.359375 1.8593674q0 1.203125 0.46875 2.015625q0.46875 0.796875 1.203125 1.1875q0.75 0.390625 1.546875 0.390625q1.046875 0 1.71875 -0.484375q0.6875 -0.5 1.09375 -1.234375l1.34375 0.65625q-0.5625 1.078125 -1.609375 1.796875q-1.03125 0.703125 -2.609375 0.703125z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.4)"><use xlink:href="#p.4" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.4" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.4"><path fill="#c9daf8" d="m192.74174 100.78206l0 0c0 -7.7669296 6.296341 -14.063271 14.063278 -14.063271l311.08603 0c3.7298584 0 7.306885 1.4816589 9.944275 4.119034c2.637329 2.6373749 4.1190186 6.214424 4.1190186 9.944237l0 56.251404c0 7.7669373 -6.2963257 14.063278 -14.063293 14.063278l-311.08603 0c-7.7669373 0 -14.063278 -6.296341 -14.063278 -14.063278z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m192.74174 100.78206l0 0c0 -7.7669296 6.296341 -14.063271 14.063278 -14.063271l311.08603 0c3.7298584 0 7.306885 1.4816589 9.944275 4.119034c2.637329 2.6373749 4.1190186 6.214424 4.1190186 9.944237l0 56.251404c0 7.7669373 -6.2963257 14.063278 -14.063293 14.063278l-311.08603 0c-7.7669373 0 -14.063278 -6.296341 -14.063278 -14.063278z" fill-rule="evenodd"/></g><path fill="#a2c4c9" d="m203.9767 122.657425l0 0c0 -4.68219 3.7956696 -8.4778595 8.4778595 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.4778595 -3.7956543 -8.4778595 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m203.9767 122.657425l0 0c0 -4.68219 3.7956696 -8.4778595 8.4778595 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.4778595 -3.7956543 -8.4778595 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m221.41891 145.47264l4.359375 -11.453125l1.53125 0l4.359375 11.453125l-1.5 0l-3.140625 -8.5625l-0.453125 -1.234375l-0.0625 0l-0.453125 1.234375l-3.140625 8.5625l-1.5 0zm4.96875 -3.15625l0 -1.28125l2.640625 0l0.453125 1.28125l-3.09375 0zm-3.109375 0l0.46875 -1.28125l2.640625 0l0 1.28125l-3.109375 0zm9.3450165 3.15625l0 -1.359375q0.046875 -0.046875 0.375 -0.375q0.34375 -0.34375 0.84375 -0.84375q0.5 -0.515625 1.03125 -1.0625q0.546875 -0.546875 1.015625 -1.03125q0.484375 -0.5 0.75 -0.796875q0.5 -0.546875 0.796875 -0.9375q0.3125 -0.390625 0.453125 -0.765625q0.140625 -0.375 0.140625 -0.875q0 -0.46875 -0.25 -0.890625q-0.234375 -0.421875 -0.6875 -0.6875q-0.453125 -0.28125 -1.140625 -0.28125q-0.640625 0 -1.078125 0.28125q-0.4375 0.265625 -0.6875 0.640625q-0.234375 0.375 -0.3125 0.6875l-1.234375 -0.5q0.09375 -0.34375 0.34375 -0.75q0.25 -0.40625 0.65625 -0.78125q0.40625 -0.375 0.984375 -0.625q0.59375 -0.25 1.359375 -0.25q1.046875 0 1.8125 0.453125q0.76564026 0.4375 1.1875153 1.15625q0.421875 0.703125 0.421875 1.53125q0 0.71875 -0.28125 1.375q-0.265625 0.640625 -0.67189026 1.171875q-0.390625 0.53125 -0.796875 0.921875q-0.21875 0.203125 -0.59375 0.578125q-0.359375 0.375 -0.78125 0.796875q-0.40625 0.421875 -0.8125 0.828125q-0.390625 0.390625 -0.6875 0.703125q-0.296875 0.296875 -0.40625 0.390625l0 0l5.1562653 0l0 1.296875l-6.9062653 0zm8.978531 0l0 -11.453125l3.453125 0q1.796875 0 3.09375 0.734375q1.296875 0.71875 2.0 2.015625q0.703125 1.296875 0.703125 2.96875q0 1.6875 -0.703125 2.984375q-0.703125 1.28125 -2.0 2.015625q-1.296875 0.734375 -3.09375 0.734375l-3.453125 0zm1.359375 -1.296875l2.09375 0q1.34375 0 2.328125 -0.515625q1.0 -0.53125 1.546875 -1.515625q0.546875 -0.984375 0.546875 -2.40625q0 -1.40625 -0.546875 -2.390625q-0.546875 -1.0 -1.546875 -1.515625q-0.984375 -0.515625 -2.328125 -0.515625l-2.09375 0l0 8.859375zm9.888641 1.296875l0 -11.453125l3.859375 0q0.953125 0 1.765625 0.4375q0.828125 0.421875 1.3125 1.1875q0.484375 0.75 0.484375 1.765625q0 0.984375 -0.484375 1.765625q-0.484375 0.765625 -1.3125 1.203125q-0.8125 0.421875 -1.765625 0.421875l-3.140625 0l0 -1.296875l3.171875 0q0.671875 0 1.140625 -0.3125q0.484375 -0.3125 0.75 -0.78125q0.265625 -0.484375 0.265625 -1.0q0 -0.515625 -0.265625 -0.984375q-0.265625 -0.484375 -0.75 -0.796875q-0.46875 -0.3125 -1.140625 -0.3125l-2.53125 0l0 10.15625l-1.359375 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m284.8796 122.657425l0 0c0 -4.68219 3.7956543 -8.4778595 8.477844 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.477844 -3.7956543 -8.477844 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m284.8796 122.657425l0 0c0 -4.68219 3.7956543 -8.4778595 8.477844 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4831238 3.7462845 2.4831238 5.994751l0 33.910416c0 4.682205 -3.7956848 8.4778595 -8.477875 8.4778595l-57.060028 0c-4.68219 0 -8.477844 -3.7956543 -8.477844 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m308.7287 145.47264l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.9180298 0l0 -11.453125l6.625 0l0 1.296875l-5.265625 0l0 10.15625l-1.359375 0zm0.71875 -4.953125l0 -1.28125l5.390625 0l0 1.28125l-5.390625 0zm7.777252 4.953125l0 -11.453125l3.859375 0q0.953125 0 1.765625 0.4375q0.828125 0.421875 1.3125 1.1875q0.484375 0.75 0.484375 1.765625q0 0.984375 -0.484375 1.765625q-0.484375 0.765625 -1.3125 1.203125q-0.8125 0.421875 -1.765625 0.421875l-3.140625 0l0 -1.296875l3.171875 0q0.671875 0 1.140625 -0.3125q0.484375 -0.3125 0.75 -0.78125q0.265625 -0.484375 0.265625 -1.0q0 -0.515625 -0.265625 -0.984375q-0.265625 -0.484375 -0.75 -0.796875q-0.46875 -0.3125 -1.140625 -0.3125l-2.53125 0l0 10.15625l-1.359375 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m365.78247 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m365.78247 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m390.90356 145.47264l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.9180298 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.9200134 0l0 -11.453125l3.453125 0q1.796875 0 3.09375 0.734375q1.296875 0.71875 2.0 2.015625q0.703125 1.296875 0.703125 2.96875q0 1.6875 -0.703125 2.984375q-0.703125 1.28125 -2.0 2.015625q-1.296875 0.734375 -3.09375 0.734375l-3.453125 0zm1.359375 -1.296875l2.09375 0q1.34375 0 2.328125 -0.515625q1.0 -0.53125 1.546875 -1.515625q0.546875 -0.984375 0.546875 -2.40625q0 -1.40625 -0.546875 -2.390625q-0.546875 -1.0 -1.546875 -1.515625q-0.984375 -0.515625 -2.328125 -0.515625l-2.09375 0l0 8.859375z" fill-rule="nonzero"/><g filter="url(#shadowFilter-p.5)"><use xlink:href="#p.5" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.5" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.5"><path fill="#6d9eeb" d="m4.8992295 317.66617l0 0c0 -5.2562256 4.2610183 -9.517242 9.51725 -9.517242l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.205597 2.7875366 6.729706l0 38.06787c0 5.2562256 -4.2609863 9.517242 -9.517273 9.517242l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.517242z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 317.66617l0 0c0 -5.2562256 4.2610183 -9.517242 9.51725 -9.517242l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.205597 2.7875366 6.729706l0 38.06787c0 5.2562256 -4.2609863 9.517242 -9.517273 9.517242l-508.0206 0c-5.2562323 0 -9.51725 -4.261017 -9.51725 -9.517242z" fill-rule="evenodd"/><path fill="#000000" d="m252.81201 342.56012l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm7.242401 0.25q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm8.549774 1.21875q-0.921875 0 -1.625 -0.296875q-0.6875 -0.296875 -1.140625 -0.796875q-0.453125 -0.5 -0.671875 -1.09375l1.203125 -0.546875q0.328125 0.734375 0.9375 1.140625q0.609375 0.40625 1.390625 0.40625q0.75 0 1.25 -0.296875q0.515625 -0.3125 0.515625 -0.90625q0 -0.375 -0.21875 -0.625q-0.203125 -0.25 -0.609375 -0.421875q-0.390625 -0.171875 -0.96875 -0.3125l-1.0 -0.265625q-0.5625 -0.15625 -1.078125 -0.4375q-0.515625 -0.296875 -0.828125 -0.75q-0.3125 -0.453125 -0.3125 -1.109375q0 -0.734375 0.421875 -1.265625q0.4375 -0.53125 1.140625 -0.8125q0.703125 -0.28125 1.515625 -0.28125q0.703125 0 1.3125 0.203125q0.625 0.203125 1.078125 0.59375q0.46875 0.390625 0.703125 0.96875l-1.171875 0.546875q-0.3125 -0.609375 -0.828125 -0.84375q-0.5 -0.25 -1.125 -0.25q-0.671875 0 -1.171875 0.296875q-0.5 0.296875 -0.5 0.8125q0 0.515625 0.40625 0.765625q0.40625 0.25 1.0 0.421875l1.1875 0.296875q1.203125 0.3125 1.8125 0.90625q0.609375 0.59375 0.609375 1.46875q0 0.765625 -0.4375 1.328125q-0.4375 0.546875 -1.171875 0.859375q-0.734375 0.296875 -1.625 0.296875zm4.0817566 -8.40625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875z" fill-rule="nonzero"/></g><path fill="#a2c4c9" d="m141.82906 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m141.82906 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m162.77774 250.34612q-0.90625 0 -1.71875 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.609375 -0.609375 -0.953125 -1.421875q-0.34375 -0.828125 -0.34375 -1.78125q0 -0.953125 0.34375 -1.765625q0.34375 -0.828125 0.953125 -1.4375q0.609375 -0.609375 1.421875 -0.9375q0.8125 -0.34375 1.71875 -0.34375q0.953125 0 1.78125 0.34375q0.84375 0.328125 1.375 0.9375l-0.71875 0.71875q-0.265625 -0.328125 -0.65625 -0.5625q-0.375 -0.234375 -0.828125 -0.34375q-0.4375 -0.125 -0.9375 -0.125q-0.6875 0 -1.3125 0.25q-0.609375 0.25 -1.09375 0.71875q-0.46875 0.453125 -0.75 1.109375q-0.265625 0.640625 -0.265625 1.4375q0 0.8125 0.28125 1.453125q0.28125 0.640625 0.75 1.109375q0.484375 0.46875 1.09375 0.71875q0.625 0.234375 1.296875 0.234375q0.578125 0 1.125 -0.15625q0.546875 -0.171875 0.984375 -0.5q0.4375 -0.34375 0.71875 -0.890625q0.296875 -0.546875 0.359375 -1.296875l-3.171875 0l0 -0.953125l4.125 0q0.03125 0.15625 0.046875 0.3125q0.015625 0.140625 0.015625 0.3125l0 0l0 0l0 0q0 0.921875 -0.3125 1.6875q-0.3125 0.765625 -0.875 1.3125q-0.5625 0.546875 -1.328125 0.84375q-0.765625 0.296875 -1.703125 0.296875zm4.551193 -0.1875l3.25 -8.59375l1.15625 0l3.265625 8.59375l-1.109375 0l-2.375 -6.421875l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.421875l-1.109375 0zm3.71875 -2.359375l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm8.400681 2.359375l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0zm8.862289 7.625l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0z" fill-rule="nonzero"/><path fill="#000000" d="m161.77573 264.3461q-0.9375 0 -1.75 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.59375 -0.609375 -0.9375 -1.421875q-0.328125 -0.828125 -0.328125 -1.78125q0 -0.953125 0.328125 -1.765625q0.34375 -0.828125 0.9375 -1.421875q0.609375 -0.609375 1.421875 -0.95310974q0.8125 -0.34375 1.75 -0.34375q0.671875 0 1.234375 0.171875q0.578125 0.15625 1.046875 0.46873474q0.484375 0.3125 0.875 0.765625l-0.734375 0.703125q-0.328125 -0.390625 -0.703125 -0.640625q-0.359375 -0.265625 -0.78125 -0.375q-0.421875 -0.125 -0.9375 -0.125q-0.921875 0 -1.703125 0.4375q-0.765625 0.421875 -1.234375 1.21875q-0.46875 0.78125 -0.46875 1.859375q0 1.0625 0.46875 1.859375q0.46875 0.796875 1.234375 1.234375q0.78125 0.421875 1.703125 0.421875q0.578125 0 1.046875 -0.15625q0.46875 -0.15625 0.875 -0.4375q0.40625 -0.296875 0.734375 -0.6875l0.75 0.71875q-0.375 0.4375 -0.90625 0.796875q-0.515625 0.34375 -1.140625 0.546875q-0.625 0.1875 -1.359375 0.1875zm4.537689 -0.1875l0 -8.593735l1.03125 0l0 8.593735l-1.03125 0zm2.641388 0l0 -6.125l1.015625 0l0 6.125l-1.015625 0zm0.5 -7.25q-0.296875 0 -0.515625 -0.21875q-0.21875 -0.21875 -0.21875 -0.515625q0 -0.31248474 0.21875 -0.51560974q0.21875 -0.21875 0.515625 -0.21875q0.3125 0 0.515625 0.21875q0.21875 0.203125 0.21875 0.51560974q0 0.296875 -0.21875 0.515625q-0.203125 0.21875 -0.515625 0.21875zm4.7770233 7.4375q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.90625l0.046875 0q0.234375 -0.4375 0.78125 -0.765625q0.546875 -0.328125 1.1875 -0.328125q1.140625 0 1.703125 0.65625q0.578125 0.65625 0.578125 1.734375l0 3.921875l-1.03125 0l0 -3.765625q0 -0.890625 -0.421875 -1.25q-0.421875 -0.375 -1.09375 -0.375q-0.515625 0 -0.90625 0.28125q-0.375 0.28125 -0.59375 0.734375q-0.203125 0.4375 -0.203125 0.9375l0 3.4375l-1.03125 0zm6.0257874 -6.125l3.578125 0l0 0.921875l-3.578125 0l0 -0.921875zm1.0625 4.515625l0 -6.234375l1.015625 0l0 5.984375q0 0.484375 0.1875 0.75q0.203125 0.25 0.671875 0.25q0.203125 0 0.359375 -0.046875q0.171875 -0.0625 0.3125 -0.15625l0 1.0q-0.15625 0.078125 -0.34375 0.109375q-0.1875 0.046875 -0.5 0.046875q-0.765625 0 -1.234375 -0.4375q-0.46875 -0.453125 -0.46875 -1.265625z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m77.69106 235.04298l0 0c0 -4.8590393 3.9390335 -8.798065 8.798073 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499481 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798073 -3.9390259 -8.798073 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m77.69106 235.04298l0 0c0 -4.8590393 3.9390335 -8.798065 8.798073 -8.798065l43.852676 0c2.3333893 0 4.571213 0.92692566 6.221176 2.576889c1.6499481 1.6499634 2.576889 3.8877869 2.576889 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.852676 0c-4.8590393 0 -8.798073 -3.9390259 -8.798073 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m101.24976 257.3461q-0.90625 0 -1.71875 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.609375 -0.60935974 -0.953125 -1.4218597q-0.34375 -0.828125 -0.34375 -1.78125q0 -0.953125 0.34375 -1.765625q0.34375 -0.828125 0.953125 -1.4375q0.609375 -0.609375 1.421875 -0.9375q0.8125 -0.34375 1.71875 -0.34375q0.953125 0 1.78125 0.34375q0.84375 0.328125 1.375 0.9375l-0.71875 0.71875q-0.265625 -0.328125 -0.65625 -0.5625q-0.375 -0.234375 -0.828125 -0.34375q-0.4375 -0.125 -0.9375 -0.125q-0.6875 0 -1.3125 0.25q-0.609375 0.25 -1.09375 0.71875q-0.46875 0.453125 -0.75 1.109375q-0.265625 0.640625 -0.265625 1.4375q0 0.8125 0.28125 1.453125q0.28125 0.640625 0.75 1.109375q0.484375 0.46875 1.09375 0.71873474q0.625 0.234375 1.296875 0.234375q0.578125 0 1.125 -0.15625q0.546875 -0.171875 0.984375 -0.49998474q0.4375 -0.34375 0.71875 -0.890625q0.296875 -0.546875 0.359375 -1.296875l-3.171875 0l0 -0.953125l4.125 0q0.03125 0.15625 0.046875 0.3125q0.015625 0.140625 0.015625 0.3125l0 0l0 0l0 0q0 0.921875 -0.3125 1.6875q-0.3125 0.765625 -0.875 1.3124847q-0.5625 0.546875 -1.328125 0.84375q-0.765625 0.296875 -1.703125 0.296875zm4.551193 -0.1875l3.25 -8.593735l1.15625 0l3.265625 8.593735l-1.109375 0l-2.375 -6.4218597l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.4218597l-1.109375 0zm3.71875 -2.3593597l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm7.4150543 2.3593597l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.75 -0.375 1.328125q-0.359375 0.5625 -0.96875 0.890625q-0.609375 0.328125 -1.328125 0.328125l-2.359375 0l0 -0.96875l2.375 0q0.5 0 0.859375 -0.234375q0.359375 -0.234375 0.5625 -0.59375q0.203125 -0.359375 0.203125 -0.75q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m205.96704 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.85266 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m205.96704 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.85266 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m226.91573 250.34612q-0.90625 0 -1.71875 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.609375 -0.609375 -0.953125 -1.421875q-0.34375 -0.828125 -0.34375 -1.78125q0 -0.953125 0.34375 -1.765625q0.34375 -0.828125 0.953125 -1.4375q0.609375 -0.609375 1.421875 -0.9375q0.8125 -0.34375 1.71875 -0.34375q0.953125 0 1.78125 0.34375q0.84375 0.328125 1.375 0.9375l-0.71875 0.71875q-0.265625 -0.328125 -0.65625 -0.5625q-0.375 -0.234375 -0.828125 -0.34375q-0.4375 -0.125 -0.9375 -0.125q-0.6875 0 -1.3125 0.25q-0.609375 0.25 -1.09375 0.71875q-0.46875 0.453125 -0.75 1.109375q-0.265625 0.640625 -0.265625 1.4375q0 0.8125 0.28125 1.453125q0.28125 0.640625 0.75 1.109375q0.484375 0.46875 1.09375 0.71875q0.625 0.234375 1.296875 0.234375q0.578125 0 1.125 -0.15625q0.546875 -0.171875 0.984375 -0.5q0.4375 -0.34375 0.71875 -0.890625q0.296875 -0.546875 0.359375 -1.296875l-3.171875 0l0 -0.953125l4.125 0q0.03125 0.15625 0.046875 0.3125q0.015625 0.140625 0.015625 0.3125l0 0l0 0l0 0q0 0.921875 -0.3125 1.6875q-0.3125 0.765625 -0.875 1.3125q-0.5625 0.546875 -1.328125 0.84375q-0.765625 0.296875 -1.703125 0.296875zm4.551193 -0.1875l3.25 -8.59375l1.15625 0l3.265625 8.59375l-1.109375 0l-2.375 -6.421875l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.421875l-1.109375 0zm3.71875 -2.359375l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm8.400681 2.359375l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0zm8.862289 7.625l0 -7.984375l1.03125 0l0 7.984375l-1.03125 0zm-2.40625 -7.625l0 -0.96875l5.828125 0l0 0.96875l-5.828125 0z" fill-rule="nonzero"/><path fill="#000000" d="m222.3267 264.3461q-0.625 0 -1.234375 -0.265625q-0.609375 -0.265625 -1.078125 -0.78125q-0.46875 -0.515625 -0.6875 -1.28125l0.96875 -0.390625q0.203125 0.75 0.734375 1.25q0.546875 0.5 1.328125 0.5q0.46875 0 0.875 -0.171875q0.40625 -0.171875 0.640625 -0.5q0.25 -0.34375 0.25 -0.828125q0 -0.4375 -0.21875 -0.734375q-0.203125 -0.296875 -0.625 -0.53125q-0.40625 -0.234375 -1.03125 -0.453125l-0.53125 -0.1875q-0.359375 -0.125 -0.734375 -0.3125q-0.359375 -0.1875 -0.671875 -0.453125q-0.296875 -0.28125 -0.484375 -0.65625q-0.171875 -0.375 -0.171875 -0.890625q0 -0.625 0.328125 -1.140625q0.34375 -0.515625 0.921875 -0.82810974q0.59375 -0.3125 1.359375 -0.3125q0.8125 0 1.359375 0.296875q0.546875 0.28125 0.859375 0.68748474q0.3125 0.40625 0.421875 0.734375l-0.953125 0.421875q-0.0625 -0.265625 -0.265625 -0.53125q-0.203125 -0.265625 -0.546875 -0.453125q-0.34375 -0.1875 -0.859375 -0.1875q-0.421875 0 -0.796875 0.171875q-0.359375 0.171875 -0.578125 0.46875q-0.21875 0.28125 -0.21875 0.671875q0 0.53125 0.421875 0.84375q0.4375 0.3125 1.171875 0.5625l0.5625 0.203125q0.390625 0.125 0.78125 0.328125q0.40625 0.1875 0.75 0.484375q0.359375 0.296875 0.5625 0.734375q0.21875 0.4375 0.21875 1.046875q0 0.671875 -0.265625 1.140625q-0.25 0.46875 -0.671875 0.78125q-0.40625 0.296875 -0.90625 0.421875q-0.5 0.140625 -0.984375 0.140625zm6.869049 0q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0zm6.5165253 0l-2.484375 -6.125l1.09375 0l1.90625 4.921875l0.015625 0l1.921875 -4.921875l1.0625 0l-2.484375 6.125l-1.03125 0zm6.9099274 0.1875q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m270.105 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499329 1.6499634 2.5768738 3.8877869 2.5768738 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85266 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m270.105 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.85266 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499329 1.6499634 2.5768738 3.8877869 2.5768738 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85266 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m281.83264 250.34612q-0.625 0 -1.234375 -0.265625q-0.609375 -0.265625 -1.078125 -0.78125q-0.46875 -0.515625 -0.6875 -1.28125l0.96875 -0.390625q0.203125 0.75 0.734375 1.25q0.546875 0.5 1.328125 0.5q0.46875 0 0.875 -0.171875q0.40625 -0.171875 0.640625 -0.5q0.25 -0.34375 0.25 -0.828125q0 -0.4375 -0.21875 -0.734375q-0.203125 -0.296875 -0.625 -0.53125q-0.40625 -0.234375 -1.03125 -0.453125l-0.53125 -0.1875q-0.359375 -0.125 -0.734375 -0.3125q-0.359375 -0.1875 -0.671875 -0.453125q-0.296875 -0.28125 -0.484375 -0.65625q-0.171875 -0.375 -0.171875 -0.890625q0 -0.625 0.328125 -1.140625q0.34375 -0.515625 0.921875 -0.828125q0.59375 -0.3125 1.359375 -0.3125q0.8125 0 1.359375 0.296875q0.546875 0.28125 0.859375 0.6875q0.3125 0.40625 0.421875 0.734375l-0.953125 0.421875q-0.0625 -0.265625 -0.265625 -0.53125q-0.203125 -0.265625 -0.546875 -0.453125q-0.34375 -0.1875 -0.859375 -0.1875q-0.421875 0 -0.796875 0.171875q-0.359375 0.171875 -0.578125 0.46875q-0.21875 0.28125 -0.21875 0.671875q0 0.53125 0.421875 0.84375q0.4375 0.3125 1.171875 0.5625l0.5625 0.203125q0.390625 0.125 0.78125 0.328125q0.40625 0.1875 0.75 0.484375q0.359375 0.296875 0.5625 0.734375q0.21875 0.4375 0.21875 1.046875q0 0.671875 -0.265625 1.140625q-0.25 0.46875 -0.671875 0.78125q-0.40625 0.296875 -0.90625 0.421875q-0.5 0.140625 -0.984375 0.140625zm6.869049 0q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm6.862915 0q-0.90625 0 -1.609375 -0.421875q-0.703125 -0.421875 -1.09375 -1.15625q-0.390625 -0.75 -0.390625 -1.671875q0 -0.9375 0.390625 -1.65625q0.390625 -0.734375 1.09375 -1.15625q0.703125 -0.4375 1.609375 -0.4375q1.03125 0 1.6875 0.484375q0.671875 0.46875 0.953125 1.203125l-0.921875 0.390625q-0.234375 -0.5625 -0.6875 -0.859375q-0.453125 -0.296875 -1.078125 -0.296875q-0.546875 0 -1.015625 0.296875q-0.453125 0.28125 -0.75 0.8125q-0.28125 0.515625 -0.28125 1.21875q0 0.703125 0.28125 1.234375q0.296875 0.515625 0.75 0.8125q0.46875 0.28125 1.015625 0.28125q0.640625 0 1.109375 -0.296875q0.46875 -0.296875 0.703125 -0.859375l0.90625 0.390625q-0.296875 0.703125 -0.96875 1.203125q-0.671875 0.484375 -1.703125 0.484375zm5.989563 0q-1.125 0 -1.703125 -0.65625q-0.5625 -0.65625 -0.5625 -1.796875l0 -3.859375l1.03125 0l0 3.703125q0 0.90625 0.40625 1.296875q0.421875 0.390625 1.046875 0.390625q0.546875 0 0.9375 -0.28125q0.40625 -0.28125 0.609375 -0.71875q0.21875 -0.453125 0.21875 -0.921875l0 -3.46875l1.015625 0l0 6.125l-0.96875 0l0 -0.890625l-0.046875 0q-0.15625 0.296875 -0.46875 0.546875q-0.296875 0.234375 -0.6875 0.375q-0.390625 0.15625 -0.828125 0.15625zm4.513275 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0zm4.6094055 0l0 -6.125l1.015625 0l0 6.125l-1.015625 0zm0.5 -7.25q-0.296875 0 -0.515625 -0.21875q-0.21875 -0.21875 -0.21875 -0.515625q0 -0.3125 0.21875 -0.515625q0.21875 -0.21875 0.515625 -0.21875q0.3125 0 0.515625 0.21875q0.21875 0.203125 0.21875 0.515625q0 0.296875 -0.21875 0.515625q-0.203125 0.21875 -0.515625 0.21875zm1.6364136 1.125l3.578125 0l0 0.921875l-3.578125 0l0 -0.921875zm1.0625 4.515625l0 -6.234375l1.015625 0l0 5.984375q0 0.484375 0.1875 0.75q0.203125 0.25 0.671875 0.25q0.203125 0 0.359375 -0.046875q0.171875 -0.0625 0.3125 -0.15625l0 1.0q-0.15625 0.078125 -0.34375 0.109375q-0.1875 0.046875 -0.5 0.046875q-0.765625 0 -1.234375 -0.4375q-0.46875 -0.453125 -0.46875 -1.265625zm5.549408 3.890625q-0.046875 0.078125 -0.09375 0.171875q-0.046875 0.109375 -0.0625 0.140625l-1.046875 0q0.0625 -0.15625 0.140625 -0.34375q0.09375 -0.1875 0.21875 -0.453125q0.0625 -0.15625 0.125 -0.296875q0.0625 -0.125 0.140625 -0.28125q0.078125 -0.15625 0.1875 -0.40625l0.84375 -1.828125l0.21875 -0.5625l1.859375 -4.546875l1.09375 0l-3.171875 7.3125q-0.046875 0.109375 -0.15625 0.34375q-0.09375 0.234375 -0.1875 0.453125q-0.078125 0.21875 -0.109375 0.296875zm0.21875 -2.53125l-2.59375 -5.875l1.109375 0l1.921875 4.546875l0.09375 0l-0.53125 1.328125z" fill-rule="nonzero"/><path fill="#000000" d="m277.8184 264.1586l0 -8.593735l1.484375 0l2.734375 7.0468597l0.046875 0l2.734375 -7.0468597l1.484375 0l0 8.593735l-1.015625 0l0 -5.453125l0.046875 -1.640625l-0.046875 0l-2.8125 7.09375l-0.828125 0l-2.8125 -7.09375l-0.046875 0l0.046875 1.640625l0 5.453125l-1.015625 0zm11.977722 0.1875q-0.6875 0 -1.203125 -0.265625q-0.515625 -0.265625 -0.8125 -0.71875q-0.296875 -0.46875 -0.296875 -1.046875q0 -0.671875 0.34375 -1.125q0.359375 -0.46875 0.9375 -0.703125q0.59375 -0.234375 1.296875 -0.234375q0.40625 0 0.75 0.0625q0.359375 0.0625 0.609375 0.15625q0.265625 0.09375 0.390625 0.171875l0 -0.375q0 -0.6875 -0.5 -1.09375q-0.484375 -0.421875 -1.1875 -0.421875q-0.5 0 -0.9375 0.234375q-0.4375 0.21875 -0.6875 0.609375l-0.78125 -0.578125q0.25 -0.359375 0.609375 -0.625q0.359375 -0.265625 0.8125 -0.40625q0.46875 -0.140625 0.984375 -0.140625q1.25 0 1.953125 0.671875q0.71875 0.65625 0.71875 1.765625l0 3.875l-0.984375 0l0 -0.875l-0.046875 0q-0.15625 0.265625 -0.453125 0.515625q-0.28125 0.234375 -0.671875 0.390625q-0.375 0.15625 -0.84375 0.15625zm0.09375 -0.890625q0.53125 0 0.96875 -0.265625q0.4375 -0.265625 0.6875 -0.703125q0.265625 -0.453125 0.265625 -0.984375q-0.265625 -0.1875 -0.6875 -0.296875q-0.40625 -0.125 -0.90625 -0.125q-0.875 0 -1.28125 0.359375q-0.40625 0.359375 -0.40625 0.875q0 0.515625 0.375 0.828125q0.390625 0.3125 0.984375 0.3125zm4.30954 0.703125l0 -6.125l0.984375 0l0 0.90625l0.046875 0q0.234375 -0.4375 0.78125 -0.765625q0.546875 -0.328125 1.1875 -0.328125q1.140625 0 1.703125 0.65625q0.578125 0.65625 0.578125 1.734375l0 3.921875l-1.03125 0l0 -3.765625q0 -0.890625 -0.421875 -1.25q-0.421875 -0.375 -1.09375 -0.375q-0.515625 0 -0.90625 0.28125q-0.375 0.28125 -0.59375 0.734375q-0.203125 0.4375 -0.203125 0.9375l0 3.4375l-1.03125 0zm8.640778 0.1875q-0.6875 0 -1.203125 -0.265625q-0.515625 -0.265625 -0.8125 -0.71875q-0.296875 -0.46875 -0.296875 -1.046875q0 -0.671875 0.34375 -1.125q0.359375 -0.46875 0.9375 -0.703125q0.59375 -0.234375 1.296875 -0.234375q0.40625 0 0.75 0.0625q0.359375 0.0625 0.609375 0.15625q0.265625 0.09375 0.390625 0.171875l0 -0.375q0 -0.6875 -0.5 -1.09375q-0.484375 -0.421875 -1.1875 -0.421875q-0.5 0 -0.9375 0.234375q-0.4375 0.21875 -0.6875 0.609375l-0.78125 -0.578125q0.25 -0.359375 0.609375 -0.625q0.359375 -0.265625 0.8125 -0.40625q0.46875 -0.140625 0.984375 -0.140625q1.25 0 1.953125 0.671875q0.71875 0.65625 0.71875 1.765625l0 3.875l-0.984375 0l0 -0.875l-0.046875 0q-0.15625 0.265625 -0.453125 0.515625q-0.28125 0.234375 -0.671875 0.390625q-0.375 0.15625 -0.84375 0.15625zm0.09375 -0.890625q0.53125 0 0.96875 -0.265625q0.4375 -0.265625 0.6875 -0.703125q0.265625 -0.453125 0.265625 -0.984375q-0.265625 -0.1875 -0.6875 -0.296875q-0.40625 -0.125 -0.90625 -0.125q-0.875 0 -1.28125 0.359375q-0.40625 0.359375 -0.40625 0.875q0 0.515625 0.375 0.828125q0.390625 0.3125 0.984375 0.3125zm6.937042 3.484375q-0.78125 0 -1.34375 -0.265625q-0.546875 -0.25 -0.890625 -0.65625q-0.34375 -0.390625 -0.484375 -0.796875l0.9375 -0.390625q0.1875 0.5 0.640625 0.84375q0.46875 0.34375 1.140625 0.34375q0.96875 0 1.484375 -0.5625q0.515625 -0.546875 0.515625 -1.546875l0 -0.6875l-0.046875 0q-0.296875 0.453125 -0.84375 0.765625q-0.53125 0.296875 -1.25 0.296875q-0.78125 0 -1.4375 -0.40625q-0.65625 -0.421875 -1.046875 -1.140625q-0.375 -0.71875 -0.375 -1.671875q0 -0.953125 0.375 -1.671875q0.390625 -0.734375 1.046875 -1.140625q0.65625 -0.40625 1.4375 -0.40625q0.71875 0 1.25 0.3125q0.546875 0.296875 0.84375 0.75l0.046875 0l0 -0.875l0.96875 0l0 5.890625q0 1.015625 -0.390625 1.6875q-0.390625 0.671875 -1.0625 1.0q-0.65625 0.328125 -1.515625 0.328125zm0 -3.578125q0.546875 0 1.0 -0.265625q0.453125 -0.28125 0.71875 -0.796875q0.28125 -0.515625 0.28125 -1.234375q0 -0.75 -0.28125 -1.25q-0.265625 -0.515625 -0.71875 -0.78125q-0.453125 -0.265625 -1.0 -0.265625q-0.53125 0 -1.0 0.28125q-0.453125 0.265625 -0.734375 0.78125q-0.265625 0.5 -0.265625 1.234375q0 0.734375 0.265625 1.25q0.28125 0.5 0.734375 0.78125q0.46875 0.265625 1.0 0.265625zm7.15094 0.984375q-0.890625 0 -1.578125 -0.40625q-0.6875 -0.421875 -1.078125 -1.15625q-0.390625 -0.734375 -0.390625 -1.671875q0 -0.875 0.359375 -1.609375q0.359375 -0.75 1.03125 -1.203125q0.671875 -0.453125 1.5625 -0.453125q0.921875 0 1.5625 0.40625q0.65625 0.390625 1.015625 1.109375q0.359375 0.703125 0.359375 1.609375q0 0.09375 -0.015625 0.1875q0 0.078125 -0.015625 0.125l-5.265625 0l0 -0.828125l4.21875 0q-0.015625 -0.25 -0.125 -0.546875q-0.09375 -0.296875 -0.328125 -0.546875q-0.21875 -0.265625 -0.5625 -0.421875q-0.328125 -0.171875 -0.84375 -0.171875q-0.59375 0 -1.03125 0.3125q-0.4375 0.296875 -0.671875 0.828125q-0.234375 0.53125 -0.234375 1.203125q0 0.78125 0.296875 1.296875q0.296875 0.515625 0.765625 0.765625q0.484375 0.25 1.0 0.25q0.671875 0 1.109375 -0.3125q0.4375 -0.328125 0.703125 -0.796875l0.859375 0.421875q-0.359375 0.703125 -1.03125 1.15625q-0.671875 0.453125 -1.671875 0.453125zm4.0216675 -0.1875l0 -6.125l0.984375 0l0 0.984375l0.046875 0q0.109375 -0.34375 0.390625 -0.59375q0.28125 -0.265625 0.640625 -0.40625q0.375 -0.15625 0.734375 -0.15625q0.28125 0 0.4375 0.03125q0.15625 0.03125 0.28125 0.078125l0 1.109375q-0.1875 -0.09375 -0.40625 -0.140625q-0.21875 -0.046875 -0.453125 -0.046875q-0.4375 0 -0.8125 0.25q-0.375 0.25 -0.59375 0.671875q-0.21875 0.421875 -0.21875 0.921875l0 3.421875l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m13.553075 235.04298l0 0c0 -4.8590393 3.9390326 -8.798065 8.798076 -8.798065l43.85267 0c2.3333893 0 4.5712204 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.5768967 3.8877869 2.5768967 6.221176l0 35.19124c0 4.85907 -3.9390335 8.798096 -8.798073 8.798096l-43.85267 0c-4.859043 0 -8.798076 -3.9390259 -8.798076 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m13.553075 235.04298l0 0c0 -4.8590393 3.9390326 -8.798065 8.798076 -8.798065l43.85267 0c2.3333893 0 4.5712204 0.92692566 6.221176 2.576889c1.6499634 1.6499634 2.5768967 3.8877869 2.5768967 6.221176l0 35.19124c0 4.85907 -3.9390335 8.798096 -8.798073 8.798096l-43.85267 0c-4.859043 0 -8.798076 -3.9390259 -8.798076 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m27.098484 257.1586l0 -8.593735l1.03125 0l0 7.6249847l3.75 0l0 0.96875l-4.78125 0zm5.7017975 0l0 -1.015625q0.03125 -0.046875 0.28125 -0.29685974q0.25 -0.25 0.625 -0.625q0.375 -0.375 0.78125 -0.78125q0.40625 -0.421875 0.75 -0.78125q0.359375 -0.375 0.5625 -0.609375q0.375 -0.40625 0.59375 -0.6875q0.234375 -0.296875 0.34375 -0.578125q0.109375 -0.28125 0.109375 -0.65625q0 -0.359375 -0.1875 -0.671875q-0.171875 -0.3125 -0.515625 -0.515625q-0.34375 -0.203125 -0.859375 -0.203125q-0.46875 0 -0.8125 0.203125q-0.328125 0.203125 -0.515625 0.484375q-0.171875 0.28125 -0.234375 0.5l-0.921875 -0.359375q0.078125 -0.265625 0.265625 -0.5625q0.1875 -0.3125 0.484375 -0.59375q0.3125 -0.28125 0.75 -0.46875q0.4375 -0.1875 1.015625 -0.1875q0.78125 0 1.34375 0.34375q0.578125 0.328125 0.890625 0.859375q0.328125 0.53125 0.328125 1.15625q0 0.53125 -0.203125 1.03125q-0.203125 0.484375 -0.5 0.875q-0.296875 0.390625 -0.609375 0.703125q-0.15625 0.15625 -0.4375 0.4375q-0.28125 0.265625 -0.59375 0.59375q-0.3125 0.3125 -0.609375 0.609375q-0.296875 0.296875 -0.515625 0.53125q-0.21875 0.21873474 -0.3125 0.29685974l0 0l3.875 0l0 0.96875l-5.171875 0zm10.749542 0.1875q-0.9375 0 -1.75 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.59375 -0.60935974 -0.9375 -1.4218597q-0.328125 -0.828125 -0.328125 -1.78125q0 -0.953125 0.328125 -1.765625q0.34375 -0.828125 0.9375 -1.421875q0.609375 -0.609375 1.421875 -0.953125q0.8125 -0.34375 1.75 -0.34375q0.671875 0 1.234375 0.171875q0.578125 0.15625 1.046875 0.46875q0.484375 0.3125 0.875 0.765625l-0.734375 0.703125q-0.328125 -0.390625 -0.703125 -0.640625q-0.359375 -0.265625 -0.78125 -0.375q-0.421875 -0.125 -0.9375 -0.125q-0.921875 0 -1.703125 0.4375q-0.765625 0.421875 -1.234375 1.21875q-0.46875 0.78125 -0.46875 1.859375q0 1.0625 0.46875 1.859375q0.46875 0.796875 1.234375 1.234375q0.78125 0.42185974 1.703125 0.42185974q0.578125 0 1.046875 -0.15625q0.46875 -0.15625 0.875 -0.43748474q0.40625 -0.296875 0.734375 -0.6875l0.75 0.71875q-0.375 0.43748474 -0.90625 0.79685974q-0.515625 0.34375 -1.140625 0.546875q-0.625 0.1875 -1.359375 0.1875zm4.0951843 -0.1875l3.25 -8.593735l1.15625 0l3.265625 8.593735l-1.109375 0l-2.375 -6.4218597l-0.328125 -0.921875l-0.046875 0l-0.34375 0.921875l-2.359375 6.4218597l-1.109375 0zm3.71875 -2.3593597l0 -0.96875l1.96875 0l0.359375 0.96875l-2.328125 0zm-2.328125 0l0.34375 -0.96875l1.984375 0l0 0.96875l-2.328125 0zm7.4150543 2.3593597l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.75 -0.375 1.328125q-0.359375 0.5625 -0.96875 0.890625q-0.609375 0.328125 -1.328125 0.328125l-2.359375 0l0 -0.96875l2.375 0q0.5 0 0.859375 -0.234375q0.359375 -0.234375 0.5625 -0.59375q0.203125 -0.359375 0.203125 -0.75q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m334.243 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85269 0c2.333374 0 4.5711975 0.92692566 6.221161 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85269 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m334.243 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.85269 0c2.333374 0 4.5711975 0.92692566 6.221161 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798065 8.798096l-43.85269 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m357.28073 257.3461q-0.625 0 -1.234375 -0.265625q-0.609375 -0.265625 -1.078125 -0.78125q-0.46875 -0.51560974 -0.6875 -1.2812347l0.96875 -0.390625q0.203125 0.75 0.734375 1.25q0.546875 0.49998474 1.328125 0.49998474q0.46875 0 0.875 -0.171875q0.40625 -0.171875 0.640625 -0.49998474q0.25 -0.34375 0.25 -0.828125q0 -0.4375 -0.21875 -0.734375q-0.203125 -0.296875 -0.625 -0.53125q-0.40625 -0.234375 -1.03125 -0.453125l-0.53125 -0.1875q-0.359375 -0.125 -0.734375 -0.3125q-0.359375 -0.1875 -0.671875 -0.453125q-0.296875 -0.28125 -0.484375 -0.65625q-0.171875 -0.375 -0.171875 -0.890625q0 -0.625 0.328125 -1.140625q0.34375 -0.515625 0.921875 -0.828125q0.59375 -0.3125 1.359375 -0.3125q0.8125 0 1.359375 0.296875q0.546875 0.28125 0.859375 0.6875q0.3125 0.40625 0.421875 0.734375l-0.953125 0.421875q-0.0625 -0.265625 -0.265625 -0.53125q-0.203125 -0.265625 -0.546875 -0.453125q-0.34375 -0.1875 -0.859375 -0.1875q-0.421875 0 -0.796875 0.171875q-0.359375 0.171875 -0.578125 0.46875q-0.21875 0.28125 -0.21875 0.671875q0 0.53125 0.421875 0.84375q0.4375 0.3125 1.171875 0.5625l0.5625 0.203125q0.390625 0.125 0.78125 0.328125q0.40625 0.1875 0.75 0.484375q0.359375 0.296875 0.5625 0.734375q0.21875 0.4375 0.21875 1.046875q0 0.671875 -0.265625 1.1406097q-0.25 0.46875 -0.671875 0.78125q-0.40625 0.296875 -0.90625 0.421875q-0.5 0.140625 -0.984375 0.140625zm4.337799 -0.1875l0 -8.593735l2.59375 0q1.34375 0 2.3125 0.546875q0.984375 0.546875 1.5 1.515625q0.53125 0.96875 0.53125 2.234375q0 1.265625 -0.53125 2.234375q-0.515625 0.95310974 -1.5 1.5156097q-0.96875 0.546875 -2.3125 0.546875l-2.59375 0zm1.03125 -0.96875l1.5625 0q1.015625 0 1.75 -0.39060974q0.75 -0.390625 1.15625 -1.125q0.40625 -0.75 0.40625 -1.8125q0 -1.0625 -0.40625 -1.796875q-0.40625 -0.75 -1.15625 -1.140625q-0.734375 -0.390625 -1.75 -0.390625l-1.5625 0l0 6.6562347zm7.4048157 0.96875l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.75 -0.375 1.328125q-0.359375 0.5625 -0.96875 0.890625q-0.609375 0.328125 -1.328125 0.328125l-2.359375 0l0 -0.96875l2.375 0q0.5 0 0.859375 -0.234375q0.359375 -0.234375 0.5625 -0.59375q0.203125 -0.359375 0.203125 -0.75q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m398.381 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.947144 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390564 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m398.381 235.04298l0 0c0 -4.8590393 3.9390259 -8.798065 8.798065 -8.798065l43.947144 0c2.3334045 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390564 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798065 -3.9390259 -8.798065 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m405.1216 257.1586l0 -8.593735l2.90625 0q0.71875 0 1.328125 0.328125q0.609375 0.328125 0.96875 0.90625q0.375 0.5625 0.375 1.3125q0 0.453125 -0.171875 0.90625q-0.171875 0.453125 -0.53125 0.828125q-0.34375 0.359375 -0.890625 0.59375q-0.546875 0.21875 -1.296875 0.21875l-2.140625 0l0 -0.953125l2.296875 0q0.453125 0 0.828125 -0.203125q0.390625 -0.203125 0.625 -0.5625q0.25 -0.359375 0.25 -0.828125q0 -0.390625 -0.203125 -0.75q-0.203125 -0.359375 -0.5625 -0.59375q-0.359375 -0.234375 -0.859375 -0.234375l-1.890625 0l0 7.6249847l-1.03125 0zm2.078125 -3.9374847l1.125 -0.0625l2.703125 3.9531097l0 0.046875l-1.203125 0l-2.625 -3.9374847zm4.9539185 3.9374847l0 -8.593735l4.96875 0l0 0.96875l-3.9375 0l0 7.6249847l-1.03125 0zm0.546875 -3.7187347l0 -0.96875l4.046875 0l0 0.96875l-4.046875 0zm9.73642 3.9062347q-0.9375 0 -1.75 -0.328125q-0.8125 -0.34375 -1.421875 -0.953125q-0.59375 -0.60935974 -0.9375 -1.4218597q-0.328125 -0.828125 -0.328125 -1.78125q0 -0.953125 0.328125 -1.765625q0.34375 -0.828125 0.9375 -1.421875q0.609375 -0.609375 1.421875 -0.953125q0.8125 -0.34375 1.75 -0.34375q0.671875 0 1.234375 0.171875q0.578125 0.15625 1.046875 0.46875q0.484375 0.3125 0.875 0.765625l-0.734375 0.703125q-0.328125 -0.390625 -0.703125 -0.640625q-0.359375 -0.265625 -0.78125 -0.375q-0.421875 -0.125 -0.9375 -0.125q-0.921875 0 -1.703125 0.4375q-0.765625 0.421875 -1.234375 1.21875q-0.46875 0.78125 -0.46875 1.859375q0 1.0625 0.46875 1.859375q0.46875 0.796875 1.234375 1.234375q0.78125 0.42185974 1.703125 0.42185974q0.578125 0 1.046875 -0.15625q0.46875 -0.15625 0.875 -0.43748474q0.40625 -0.296875 0.734375 -0.6875l0.75 0.71875q-0.375 0.43748474 -0.90625 0.79685974q-0.515625 0.34375 -1.140625 0.546875q-0.625 0.1875 -1.359375 0.1875zm7.2457886 0q-0.921875 0 -1.640625 -0.421875q-0.703125 -0.4375 -1.109375 -1.1718597q-0.390625 -0.734375 -0.390625 -1.65625q0 -0.90625 0.390625 -1.640625q0.40625 -0.75 1.109375 -1.171875q0.71875 -0.4375 1.640625 -0.4375q0.921875 0 1.625 0.4375q0.71875 0.4375 1.109375 1.1875q0.40625 0.734375 0.40625 1.625q0 0.921875 -0.40625 1.65625q-0.390625 0.73435974 -1.109375 1.1718597q-0.703125 0.421875 -1.625 0.421875zm0 -0.921875q0.546875 0 1.03125 -0.265625q0.484375 -0.28123474 0.78125 -0.79685974q0.3125 -0.53125 0.3125 -1.265625q0 -0.734375 -0.3125 -1.25q-0.296875 -0.53125 -0.78125 -0.796875q-0.484375 -0.28125 -1.03125 -0.28125q-0.546875 0 -1.046875 0.28125q-0.484375 0.265625 -0.796875 0.796875q-0.296875 0.515625 -0.296875 1.25q0 0.734375 0.296875 1.265625q0.3125 0.515625 0.796875 0.79685974q0.5 0.265625 1.046875 0.265625zm4.203949 0.734375l0 -6.1249847l0.984375 0l0 0.90625l0.046875 0q0.15625 -0.296875 0.453125 -0.546875q0.296875 -0.25 0.671875 -0.390625q0.375 -0.15625 0.78125 -0.15625q0.6875 0 1.171875 0.328125q0.5 0.328125 0.703125 0.859375q0.3125 -0.515625 0.84375 -0.84375q0.53125 -0.34375 1.265625 -0.34375q1.09375 0 1.609375 0.671875q0.515625 0.65625 0.515625 1.71875l0 3.9218597l-1.015625 0l0 -3.7656097q0 -0.890625 -0.359375 -1.25q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.84375 0.28125q-0.359375 0.265625 -0.578125 0.703125q-0.203125 0.4375 -0.203125 0.953125l0 3.4531097l-1.015625 0l0 -3.7499847q0 -0.890625 -0.359375 -1.265625q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.828125 0.28125q-0.359375 0.265625 -0.578125 0.71875q-0.203125 0.4375 -0.203125 0.953125l0 3.4374847l-1.03125 0zm10.500061 0l0 -6.1249847l0.984375 0l0 0.90625l0.046875 0q0.15625 -0.296875 0.453125 -0.546875q0.296875 -0.25 0.671875 -0.390625q0.375 -0.15625 0.78125 -0.15625q0.6875 0 1.171875 0.328125q0.5 0.328125 0.703125 0.859375q0.3125 -0.515625 0.84375 -0.84375q0.53125 -0.34375 1.265625 -0.34375q1.09375 0 1.609375 0.671875q0.515625 0.65625 0.515625 1.71875l0 3.9218597l-1.015625 0l0 -3.7656097q0 -0.890625 -0.359375 -1.25q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.84375 0.28125q-0.359375 0.265625 -0.578125 0.703125q-0.203125 0.4375 -0.203125 0.953125l0 3.4531097l-1.015625 0l0 -3.7499847q0 -0.890625 -0.359375 -1.265625q-0.359375 -0.375 -1.015625 -0.375q-0.46875 0 -0.828125 0.28125q-0.359375 0.265625 -0.578125 0.71875q-0.203125 0.4375 -0.203125 0.953125l0 3.4374847l-1.03125 0z" fill-rule="nonzero"/><g filter="url(#shadowFilter-p.6)"><use xlink:href="#p.6" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.6" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.6"><path fill="#f6b26b" d="m1.2233764 468.88928l0 0c0 -5.2562256 4.2610183 -9.517242 9.517251 -9.517242l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729721 2.7875366c1.7848206 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.261017 9.517242 -9.517258 9.517242l-235.35919 0c-5.2562327 0 -9.517251 -4.2609863 -9.517251 -9.517242z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m1.2233764 468.88928l0 0c0 -5.2562256 4.2610183 -9.517242 9.517251 -9.517242l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729721 2.7875366c1.7848206 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.261017 9.517242 -9.517258 9.517242l-235.35919 0c-5.2562327 0 -9.517251 -4.2609863 -9.517251 -9.517242z" fill-rule="evenodd"/><path fill="#000000" d="m99.084404 494.0332q-1.265625 0 -2.34375 -0.453125q-1.078125 -0.453125 -1.890625 -1.25q-0.8125 -0.8125 -1.265625 -1.90625q-0.4375 -1.109375 -0.4375 -2.375q0 -1.28125 0.4375 -2.359375q0.453125 -1.09375 1.265625 -1.890625q0.8125 -0.8125 1.890625 -1.265625q1.078125 -0.46875 2.34375 -0.46875q0.875 0 1.640625 0.234375q0.765625 0.21875 1.40625 0.640625q0.640625 0.40625 1.140625 1.0l-0.96875 0.953125q-0.4375 -0.53125 -0.921875 -0.859375q-0.484375 -0.34375 -1.0625 -0.5q-0.5625 -0.171875 -1.234375 -0.171875q-1.234375 0 -2.28125 0.578125q-1.03125 0.578125 -1.65625 1.625q-0.625 1.046875 -0.625 2.484375q0 1.4375 0.625 2.5q0.625 1.046875 1.65625 1.625q1.046875 0.5625 2.28125 0.5625q0.75 0 1.375 -0.203125q0.640625 -0.203125 1.171875 -0.578125q0.546875 -0.390625 0.984375 -0.921875l1.0 0.953125q-0.5 0.59375 -1.203125 1.0625q-0.6875 0.46875 -1.53125 0.734375q-0.84375 0.25 -1.796875 0.25zm9.645386 0q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.6260223 0.96875l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.013519 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm4.980629 2.140625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm9.27002 0.25q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.706024 0.96875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.376007 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm6.9210052 0.25q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.383011 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.7)"><use xlink:href="#p.7" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.7" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.7"><path fill="#f6b26b" d="m4.684006 544.50085l0 0c0 -5.2562866 4.2610183 -9.517273 9.51725 -9.517273l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729706 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2610016 9.517273 -9.517242 9.517273l-235.35919 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.684006 544.50085l0 0c0 -5.2562866 4.2610183 -9.517273 9.51725 -9.517273l235.35919 0c2.5241394 0 4.9448853 1.0026855 6.729706 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2610016 9.517273 -9.517242 9.517273l-235.35919 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path fill="#000000" d="m112.98605 569.3948l0 -11.453125l3.875 0q0.953125 0 1.765625 0.4375q0.8125 0.421875 1.296875 1.1875q0.484375 0.75 0.484375 1.765625q0 0.609375 -0.234375 1.21875q-0.21875 0.59375 -0.6875 1.09375q-0.46875 0.484375 -1.203125 0.78125q-0.71875 0.296875 -1.734375 0.296875l-2.84375 0l0 -1.265625l3.078125 0q0.578125 0 1.09375 -0.265625q0.515625 -0.28125 0.828125 -0.75q0.328125 -0.484375 0.328125 -1.109375q0 -0.515625 -0.265625 -0.984375q-0.265625 -0.484375 -0.75 -0.796875q-0.46875 -0.3125 -1.140625 -0.3125l-2.53125 0l0 10.15625l-1.359375 0zm2.765625 -5.25l1.484375 -0.078125l3.625 5.265625l0 0.0625l-1.609375 0l-3.5 -5.25zm8.794525 5.5q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm9.150391 1.1875q-1.109375 0 -2.0 -0.546875q-0.890625 -0.5625 -1.40625 -1.53125q-0.5 -0.984375 -0.5 -2.25q0 -1.265625 0.5 -2.234375q0.515625 -0.984375 1.40625 -1.546875q0.890625 -0.5625 2.0 -0.5625q0.65625 0 1.1875 0.21875q0.546875 0.203125 0.953125 0.546875q0.421875 0.328125 0.640625 0.71875l0.0625 0l-0.0625 -1.140625l0 -3.375l1.359375 0l0 11.453125l-1.296875 0l0 -1.203125l-0.0625 0q-0.21875 0.375 -0.640625 0.71875q-0.40625 0.328125 -0.953125 0.53125q-0.53125 0.203125 -1.1875 0.203125zm0.140625 -1.21875q0.703125 0 1.3125 -0.375q0.625 -0.390625 1.0 -1.078125q0.390625 -0.703125 0.390625 -1.65625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -1.0 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.703125 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.65625q0 0.9375 0.375 1.640625q0.390625 0.703125 1.0 1.09375q0.625 0.375 1.328125 0.375zm6.147644 0.96875l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm6.4891357 9.90625q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.8)"><use xlink:href="#p.8" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.8" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.8"><path fill="#1155cc" d="m274.31787 468.88928l0 0c0 -5.2562256 4.261017 -9.517242 9.517242 -9.517242l234.94974 0c2.52417 0 4.9448853 1.0026855 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.2610474 9.517242 -9.517273 9.517242l-234.94974 0c-5.2562256 0 -9.517242 -4.2609863 -9.517242 -9.517242z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m274.31787 468.88928l0 0c0 -5.2562256 4.261017 -9.517242 9.517242 -9.517242l234.94974 0c2.52417 0 4.9448853 1.0026855 6.7297363 2.7875366c1.7848511 1.7848206 2.7875366 4.2055664 2.7875366 6.729706l0 38.06784c0 5.256256 -4.2610474 9.517242 -9.517273 9.517242l-234.94974 0c-5.2562256 0 -9.517242 -4.2609863 -9.517242 -9.517242z" fill-rule="evenodd"/><path fill="#000000" d="m344.86313 493.7832l-4.0625 -11.453125l1.515625 0l2.828125 8.296875l0.375 1.140625l0.078125 0l0.390625 -1.140625l2.96875 -8.296875l1.5 0l-4.1875 11.453125l-1.40625 0zm6.635376 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.296875l0.078125 0q0.203125 -0.5 0.578125 -0.828125q0.375 -0.34375 0.875 -0.515625q0.515625 -0.1875 1.03125 -0.1875q0.234375 0 0.34375 0.015625q0.125 0 0.234375 0.03125l0 1.40625q-0.15625 -0.03125 -0.34375 -0.046875q-0.171875 -0.03125 -0.390625 -0.03125q-0.6875 0 -1.21875 0.3125q-0.53125 0.3125 -0.828125 0.875q-0.28125 0.5625 -0.28125 1.296875l0 4.53125l-1.359375 0zm5.5 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm7.9512634 2.390625q-1.5 0 -2.265625 -0.875q-0.765625 -0.875 -0.765625 -2.40625l0 -5.125l1.359375 0l0 4.921875q0 1.21875 0.5625 1.75q0.5625 0.515625 1.390625 0.515625q0.71875 0 1.25 -0.375q0.53125 -0.390625 0.8125 -0.96875q0.296875 -0.59375 0.296875 -1.234375l0 -4.609375l1.359375 0l0 8.15625l-1.296875 0l0 -1.1875l-0.0625 0q-0.203125 0.390625 -0.625 0.71875q-0.40625 0.328125 -0.921875 0.515625q-0.515625 0.203125 -1.09375 0.203125zm8.536652 0q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.761627 0.9375l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm12.40863 0.25q-1.265625 0 -2.34375 -0.453125q-1.078125 -0.453125 -1.890625 -1.25q-0.8125 -0.8125 -1.265625 -1.90625q-0.4375 -1.109375 -0.4375 -2.375q0 -1.28125 0.4375 -2.359375q0.453125 -1.09375 1.265625 -1.890625q0.8125 -0.8125 1.890625 -1.265625q1.078125 -0.46875 2.34375 -0.46875q0.875 0 1.640625 0.234375q0.765625 0.21875 1.40625 0.640625q0.640625 0.40625 1.140625 1.0l-0.96875 0.953125q-0.4375 -0.53125 -0.921875 -0.859375q-0.484375 -0.34375 -1.0625 -0.5q-0.5625 -0.171875 -1.234375 -0.171875q-1.234375 0 -2.28125 0.578125q-1.03125 0.578125 -1.65625 1.625q-0.625 1.046875 -0.625 2.484375q0 1.4375 0.625 2.5q0.625 1.046875 1.65625 1.625q1.046875 0.5625 2.28125 0.5625q0.75 0 1.375 -0.203125q0.640625 -0.203125 1.171875 -0.578125q0.546875 -0.390625 0.984375 -0.921875l1.0 0.953125q-0.5 0.59375 -1.203125 1.0625q-0.6875 0.46875 -1.53125 0.734375q-0.84375 0.25 -1.796875 0.25zm9.645386 0q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.6260376 0.96875l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.013519 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm4.9806213 2.140625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm9.27002 0.25q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.706024 0.96875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm3.376007 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm6.9210205 0.25q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.3829956 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0z" fill-rule="nonzero"/></g><g filter="url(#shadowFilter-p.9)"><use xlink:href="#p.9" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.9" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.9"><path fill="#1155cc" d="m277.77325 544.50085l0 0c0 -5.2562866 4.261017 -9.517273 9.517242 -9.517273l235.35922 0c2.524109 0 4.9448853 1.0026855 6.7296753 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2609863 9.517273 -9.517212 9.517273l-235.35922 0c-5.2562256 0 -9.517242 -4.2610474 -9.517242 -9.517273z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m277.77325 544.50085l0 0c0 -5.2562866 4.261017 -9.517273 9.517242 -9.517273l235.35922 0c2.524109 0 4.9448853 1.0026855 6.7296753 2.7875366c1.7848511 1.7848511 2.7875366 4.2055664 2.7875366 6.7297363l0 38.06781c0 5.2562256 -4.2609863 9.517273 -9.517212 9.517273l-235.35922 0c-5.2562256 0 -9.517242 -4.2610474 -9.517242 -9.517273z" fill-rule="evenodd"/><path fill="#000000" d="m348.32324 569.3948l-4.0625 -11.453125l1.515625 0l2.828125 8.296875l0.375 1.140625l0.078125 0l0.390625 -1.140625l2.96875 -8.296875l1.5 0l-4.1875 11.453125l-1.40625 0zm6.635376 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.296875l0.078125 0q0.203125 -0.5 0.578125 -0.828125q0.375 -0.34375 0.875 -0.515625q0.515625 -0.1875 1.03125 -0.1875q0.234375 0 0.34375 0.015625q0.125 0 0.234375 0.03125l0 1.40625q-0.15625 -0.03125 -0.34375 -0.046875q-0.171875 -0.03125 -0.390625 -0.03125q-0.6875 0 -1.21875 0.3125q-0.53125 0.3125 -0.828125 0.875q-0.28125 0.5625 -0.28125 1.296875l0 4.53125l-1.359375 0zm5.5 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm7.9512634 2.390625q-1.5 0 -2.265625 -0.875q-0.765625 -0.875 -0.765625 -2.40625l0 -5.125l1.359375 0l0 4.921875q0 1.21875 0.5625 1.75q0.5625 0.515625 1.390625 0.515625q0.71875 0 1.25 -0.375q0.53125 -0.390625 0.8125 -0.96875q0.296875 -0.59375 0.296875 -1.234375l0 -4.609375l1.359375 0l0 8.15625l-1.296875 0l0 -1.1875l-0.0625 0q-0.203125 0.390625 -0.625 0.71875q-0.40625 0.328125 -0.921875 0.515625q-0.515625 0.203125 -1.09375 0.203125zm8.536652 0q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.761627 0.9375l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm7.3536377 0l0 -11.453125l1.359375 0l0 10.15625l5.0 0l0 1.296875l-6.359375 0zm8.003387 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.976013 0l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.5 -1.65625l0 -1.75l4.6875 -4.75l1.734375 0l0 0.0625l-6.421875 6.4375zm4.890625 1.65625l-2.953125 -4.390625l0.96875 -0.9375l3.609375 5.265625l0 0.0625l-1.625 0zm6.6670227 0l0 -11.453125l1.359375 0l0 10.15625l5.0 0l0 1.296875l-6.359375 0zm10.472137 0.25q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm7.9297485 3.984375q-0.046875 0.109375 -0.109375 0.234375q-0.0625 0.140625 -0.078125 0.171875l-1.40625 0q0.09375 -0.203125 0.203125 -0.453125q0.109375 -0.25 0.265625 -0.59375q0.09375 -0.21875 0.171875 -0.390625q0.09375 -0.171875 0.1875 -0.390625q0.109375 -0.21875 0.25 -0.53125l1.125 -2.4375l0.296875 -0.75l2.46875 -6.0625l1.46875 0l-4.21875 9.734375q-0.0625 0.15625 -0.203125 0.46875q-0.140625 0.3125 -0.265625 0.59375q-0.109375 0.296875 -0.15625 0.40625zm0.296875 -3.390625l-3.453125 -7.8125l1.46875 0l2.5625 6.0625l0.140625 0l-0.71875 1.75zm9.048004 0.59375q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.383026 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0z" fill-rule="nonzero"/></g><path fill="#a2c4c9" d="m462.61346 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.947144 0c2.333374 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m462.61346 235.04298l0 0c0 -4.8590393 3.9390564 -8.798065 8.798096 -8.798065l43.947144 0c2.333374 0 4.571228 0.92692566 6.2211914 2.576889c1.6499634 1.6499634 2.5769043 3.8877869 2.5769043 6.221176l0 35.19124c0 4.85907 -3.9390259 8.798096 -8.798096 8.798096l-43.947144 0c-4.8590393 0 -8.798096 -3.9390259 -8.798096 -8.798096z" fill-rule="evenodd"/><path fill="#000000" d="m490.619 257.23672q-0.3125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.3125 0.21875 -0.53123474q0.234375 -0.21875 0.546875 -0.21875q0.328125 0 0.546875 0.21875q0.234375 0.21873474 0.234375 0.53123474q0 0.328125 -0.234375 0.5625q-0.21875 0.21875 -0.546875 0.21875zm2.765625 0q-0.3125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.3125 0.21875 -0.53123474q0.234375 -0.21875 0.546875 -0.21875q0.328125 0 0.546875 0.21875q0.234375 0.21873474 0.234375 0.53123474q0 0.328125 -0.234375 0.5625q-0.21875 0.21875 -0.546875 0.21875zm2.75 0q-0.3125 0 -0.546875 -0.21875q-0.21875 -0.234375 -0.21875 -0.5625q0 -0.3125 0.21875 -0.53123474q0.234375 -0.21875 0.546875 -0.21875q0.328125 0 0.546875 0.21875q0.234375 0.21873474 0.234375 0.53123474q0 0.328125 -0.234375 0.5625q-0.21875 0.21875 -0.546875 0.21875z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m446.68536 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m446.68536 122.657425l0 0c0 -4.68219 3.7956848 -8.4778595 8.477875 -8.4778595l57.060028 0c2.2484741 0 4.404846 0.89320374 5.994751 2.4831085c1.5899048 1.5899048 2.4830933 3.7462845 2.4830933 5.994751l0 33.910416c0 4.682205 -3.7956543 8.4778595 -8.477844 8.4778595l-57.060028 0c-4.68219 0 -8.477875 -3.7956543 -8.477875 -8.4778595z" fill-rule="evenodd"/><path fill="#000000" d="m480.01547 145.56639q-0.4375 0 -0.734375 -0.296875q-0.296875 -0.296875 -0.296875 -0.71875q0 -0.421875 0.296875 -0.71875q0.296875 -0.296875 0.734375 -0.296875q0.4375 0 0.71875 0.296875q0.296875 0.296875 0.296875 0.71875q0 0.421875 -0.296875 0.71875q-0.28125 0.296875 -0.71875 0.296875zm3.6875 0q-0.4375 0 -0.734375 -0.296875q-0.296875 -0.296875 -0.296875 -0.71875q0 -0.421875 0.296875 -0.71875q0.296875 -0.296875 0.734375 -0.296875q0.4375 0 0.71875 0.296875q0.296875 0.296875 0.296875 0.71875q0 0.421875 -0.296875 0.71875q-0.28125 0.296875 -0.71875 0.296875zm3.671875 0q-0.4375 0 -0.734375 -0.296875q-0.296875 -0.296875 -0.296875 -0.71875q0 -0.421875 0.296875 -0.71875q0.296875 -0.296875 0.734375 -0.296875q0.4375 0 0.71875 0.296875q0.296875 0.296875 0.296875 0.71875q0 0.421875 -0.296875 0.71875q-0.28125 0.296875 -0.71875 0.296875z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m268.4268 284.34842l0 23.811005" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m268.4268 284.34842l0 23.811005" fill-rule="evenodd"/><g filter="url(#shadowFilter-p.10)"><use xlink:href="#p.10" transform="matrix(1.0 0.0 0.0 1.0 0.0 2.0)"/></g><defs><filter id="shadowFilter-p.10" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" stdDeviation="2.0" result="blur"/><feComponentTransfer in="blur" color-interpolation-filters="sRGB"><feFuncR type="linear" slope="0" intercept="0.0"/><feFuncG type="linear" slope="0" intercept="0.0"/><feFuncB type="linear" slope="0" intercept="0.0"/><feFuncA type="linear" slope="0.5" intercept="0"/></feComponentTransfer></filter></defs><g id="p.10"><path fill="#3c78d8" d="m4.8992295 393.27774l0 0c0 -5.256256 4.2610183 -9.517273 9.51725 -9.517273l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848511 2.7875366 4.205597 2.7875366 6.7297363l0 38.06784c0 5.2562256 -4.2609863 9.517273 -9.517273 9.517273l-508.0206 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m4.8992295 393.27774l0 0c0 -5.256256 4.2610183 -9.517273 9.51725 -9.517273l508.0206 0c2.52417 0 4.9448853 1.0027161 6.7297363 2.7875366c1.7848511 1.7848511 2.7875366 4.205597 2.7875366 6.7297363l0 38.06784c0 5.2562256 -4.2609863 9.517273 -9.517273 9.517273l-508.0206 0c-5.2562323 0 -9.51725 -4.2610474 -9.51725 -9.517273z" fill-rule="evenodd"/><path fill="#000000" d="m220.21194 418.17166l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm0.71875 -5.21875l0 -1.296875l6.84375 0l0 1.296875l-6.84375 0zm6.53125 5.21875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm9.133026 0.25q-1.265625 0 -2.34375 -0.453125q-1.078125 -0.453125 -1.890625 -1.25q-0.8125 -0.8125 -1.265625 -1.90625q-0.4375 -1.109375 -0.4375 -2.375q0 -1.28125 0.4375 -2.359375q0.453125 -1.09375 1.265625 -1.890625q0.8125 -0.8125 1.890625 -1.265625q1.078125 -0.46875 2.34375 -0.46875q0.875 0 1.640625 0.234375q0.765625 0.21875 1.40625 0.640625q0.640625 0.40625 1.140625 1.0l-0.96875 0.953125q-0.4375 -0.53125 -0.921875 -0.859375q-0.484375 -0.34375 -1.0625 -0.5q-0.5625 -0.171875 -1.234375 -0.171875q-1.234375 0 -2.28125 0.578125q-1.03125 0.578125 -1.65625 1.625q-0.625 1.046875 -0.625 2.484375q0 1.4375 0.625 2.5q0.625 1.046875 1.65625 1.625q1.046875 0.5625 2.28125 0.5625q0.75 0 1.375 -0.203125q0.640625 -0.203125 1.171875 -0.578125q0.546875 -0.390625 0.984375 -0.921875l1.0 0.953125q-0.5 0.59375 -1.203125 1.0625q-0.6875 0.46875 -1.53125 0.734375q-0.84375 0.25 -1.796875 0.25zm6.481018 -0.25l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm9.33577 0l0 -10.640625l1.359375 0l0 10.640625l-1.359375 0zm-3.203125 -10.15625l0 -1.296875l7.7656097 0l0 1.296875l-7.7656097 0zm8.241745 10.15625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm8.273773 0.25q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.7616577 0.9375l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm11.739746 0.25q-0.921875 0 -1.625 -0.296875q-0.6875 -0.296875 -1.140625 -0.796875q-0.453125 -0.5 -0.671875 -1.09375l1.203125 -0.546875q0.328125 0.734375 0.9375 1.140625q0.609375 0.40625 1.390625 0.40625q0.75 0 1.25 -0.296875q0.515625 -0.3125 0.515625 -0.90625q0 -0.375 -0.21875 -0.625q-0.203125 -0.25 -0.609375 -0.421875q-0.390625 -0.171875 -0.96875 -0.3125l-1.0 -0.265625q-0.5625 -0.15625 -1.078125 -0.4375q-0.515625 -0.296875 -0.828125 -0.75q-0.3125 -0.453125 -0.3125 -1.109375q0 -0.734375 0.421875 -1.265625q0.4375 -0.53125 1.140625 -0.8125q0.703125 -0.28125 1.515625 -0.28125q0.703125 0 1.3125 0.203125q0.625 0.203125 1.078125 0.59375q0.46875 0.390625 0.703125 0.96875l-1.171875 0.546875q-0.3125 -0.609375 -0.828125 -0.84375q-0.5 -0.25 -1.125 -0.25q-0.671875 0 -1.171875 0.296875q-0.5 0.296875 -0.5 0.8125q0 0.515625 0.40625 0.765625q0.40625 0.25 1.0 0.421875l1.1875 0.296875q1.203125 0.3125 1.8125 0.90625q0.609375 0.59375 0.609375 1.46875q0 0.765625 -0.4375 1.328125q-0.4375 0.546875 -1.171875 0.859375q-0.734375 0.296875 -1.625 0.296875zm4.6442566 3.203125l0 -11.609375l1.28125 0l0 1.21875l0.078125 0q0.21875 -0.390625 0.625 -0.71875q0.40625 -0.34375 0.953125 -0.546875q0.546875 -0.21875 1.203125 -0.21875q1.109375 0 1.984375 0.5625q0.890625 0.5625 1.40625 1.546875q0.515625 0.96875 0.515625 2.234375q0 1.265625 -0.515625 2.25q-0.515625 0.96875 -1.40625 1.53125q-0.875 0.546875 -1.984375 0.546875q-0.984375 0 -1.71875 -0.4375q-0.734375 -0.453125 -1.0625 -1.015625l-0.078125 0l0.078125 1.125l0 3.53125l-1.359375 0zm4.0 -4.421875q0.703125 0 1.3125 -0.375q0.609375 -0.390625 0.984375 -1.09375q0.390625 -0.703125 0.390625 -1.640625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -0.984375 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.71875 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.390625 0.6875 -0.390625 1.65625q0 0.953125 0.390625 1.65625q0.390625 0.6875 1.0 1.078125q0.609375 0.375 1.328125 0.375zm9.382019 1.21875q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.626007 0.96875l0 -8.15625l1.28125 0l0 1.296875l0.078125 0q0.203125 -0.5 0.578125 -0.828125q0.375 -0.34375 0.875 -0.515625q0.515625 -0.1875 1.03125 -0.1875q0.234375 0 0.34375 0.015625q0.125 0 0.234375 0.03125l0 1.40625q-0.15625 -0.03125 -0.34375 -0.046875q-0.171875 -0.03125 -0.390625 -0.03125q-0.6875 0 -1.21875 0.3125q-0.53125 0.3125 -0.828125 0.875q-0.28125 0.5625 -0.28125 1.296875l0 4.53125l-1.359375 0zm5.5 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875z" fill-rule="nonzero"/></g><path fill="#000000" fill-opacity="0.0" d="m268.4268 365.25128l0 18.519684" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m268.4268 365.25128l0 18.519684" fill-rule="evenodd"/><path fill="#a4c2f4" d="m205.13545 82.27259l314.42523 0c3.2869873 0 6.439392 1.3057632 8.763672 3.6300278c2.3242188 2.3242722 3.630005 5.4766617 3.630005 8.763672l0 12.3937c0 0.0015640259 -0.0012817383 0.0028305054 -0.0028076172 0.0028305054l-339.20978 -0.0028305054l0 0c-0.0015563965 0 -0.002822876 -0.0012664795 -0.002822876 -0.002822876l0.002822876 -12.390877l0 0c0 -6.8448486 5.5488586 -12.3937 12.393707 -12.3937z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m205.13545 82.27259l314.42523 0c3.2869873 0 6.439392 1.3057632 8.763672 3.6300278c2.3242188 2.3242722 3.630005 5.4766617 3.630005 8.763672l0 12.3937c0 0.0015640259 -0.0012817383 0.0028305054 -0.0028076172 0.0028305054l-339.20978 -0.0028305054l0 0c-0.0015563965 0 -0.002822876 -0.0012664795 -0.002822876 -0.002822876l0.002822876 -12.390877l0 0c0 -6.8448486 5.5488586 -12.3937 12.393707 -12.3937z" fill-rule="evenodd"/><path fill="#000000" d="m339.0376 99.98258l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.213104 5.5l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.619873 3.796875l0 -6.59375l-1.140625 0l0 -1.0l1.140625 0l0 -0.8125q0 -0.765625 0.125 -1.140625q0.1875 -0.5 0.65625 -0.8125q0.46875 -0.3125 1.3125 -0.3125q0.546875 0 1.203125 0.125l-0.1875 1.125q-0.40625 -0.0625 -0.765625 -0.0625q-0.578125 0 -0.828125 0.25q-0.234375 0.25 -0.234375 0.9375l0 0.703125l1.46875 0l0 1.0l-1.46875 0l0 6.59375l-1.28125 0zm3.7594604 -9.015625l0 -1.46875l1.296875 0l0 1.46875l-1.296875 0zm0 9.015625l0 -7.59375l1.296875 0l0 7.59375l-1.296875 0zm3.2249146 0l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm8.490509 -2.453125l1.328125 0.171875q-0.3125 1.171875 -1.171875 1.8125q-0.84375 0.640625 -2.171875 0.640625q-1.671875 0 -2.65625 -1.015625q-0.96875 -1.03125 -0.96875 -2.890625q0 -1.921875 0.984375 -2.96875q1.0 -1.0625 2.578125 -1.0625q1.515625 0 2.484375 1.03125q0.96875 1.03125 0.96875 2.921875q0 0.109375 -0.015625 0.34375l-5.65625 0q0.0625 1.25 0.703125 1.921875q0.640625 0.65625 1.59375 0.65625q0.703125 0 1.203125 -0.359375q0.5 -0.375 0.796875 -1.203125zm-4.234375 -2.078125l4.25 0q-0.09375 -0.953125 -0.484375 -1.4375q-0.625 -0.75 -1.609375 -0.75q-0.875 0 -1.484375 0.59375q-0.609375 0.59375 -0.671875 1.59375zm6.666748 2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/><path fill="#6d9eeb" d="m17.29293 195.78148l502.26773 0c3.2869873 0 6.439392 1.3057556 8.763672 3.6300201c2.3242188 2.3242798 3.630005 5.4766693 3.630005 8.763672l0 12.393707c0 0.0024261475 -0.001953125 0.0043945312 -0.0043945312 0.0043945312l-527.0507 -0.0043945312l0 0c-0.0024256706 0 -0.004392147 -0.0019683838 -0.004392147 -0.0043945312l0.004392147 -12.389313l0 0c0 -6.8448486 5.5488486 -12.393692 12.393702 -12.393692z" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m17.29293 195.78148l502.26773 0c3.2869873 0 6.439392 1.3057556 8.763672 3.6300201c2.3242188 2.3242798 3.630005 5.4766693 3.630005 8.763672l0 12.393707c0 0.0024261475 -0.001953125 0.0043945312 -0.0043945312 0.0043945312l-527.0507 -0.0043945312l0 0c-0.0024256706 0 -0.004392147 -0.0019683838 -0.004392147 -0.0043945312l0.004392147 -12.389313l0 0c0 -6.8448486 5.5488486 -12.393692 12.393702 -12.393692z" fill-rule="evenodd"/><path fill="#000000" d="m239.0048 213.49147l0 -10.484375l3.96875 0q1.046875 0 1.59375 0.09375q0.765625 0.125 1.28125 0.484375q0.53125 0.359375 0.84375 1.015625q0.328125 0.65625 0.328125 1.4375q0 1.328125 -0.859375 2.265625q-0.84375 0.921875 -3.078125 0.921875l-2.6875 0l0 4.265625l-1.390625 0zm1.390625 -5.5l2.71875 0q1.34375 0 1.90625 -0.5q0.5625 -0.5 0.5625 -1.40625q0 -0.671875 -0.328125 -1.140625q-0.328125 -0.46875 -0.875 -0.609375q-0.359375 -0.09375 -1.296875 -0.09375l-2.6875 0l0 3.75zm8.213104 5.5l0 -7.59375l1.15625 0l0 1.140625q0.453125 -0.796875 0.828125 -1.046875q0.375 -0.265625 0.8125 -0.265625q0.65625 0 1.328125 0.40625l-0.4375 1.203125q-0.46875 -0.28125 -0.953125 -0.28125q-0.421875 0 -0.765625 0.25q-0.328125 0.25 -0.46875 0.703125q-0.21875 0.6875 -0.21875 1.5l0 3.984375l-1.28125 0zm4.4118958 -3.796875q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.3906097 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.5937347 0 -2.5781097 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.6093597 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96873474 0 -1.6093597 0.71875q-0.625 0.71875 -0.625 2.1875zm10.119858 2.640625l0.1875 1.140625q-0.546875 0.109375 -0.984375 0.109375q-0.6875 0 -1.078125 -0.21875q-0.390625 -0.21875 -0.546875 -0.578125q-0.15625 -0.359375 -0.15625 -1.515625l0 -4.375l-0.953125 0l0 -1.0l0.953125 0l0 -1.890625l1.28125 -0.765625l0 2.65625l1.296875 0l0 1.0l-1.296875 0l0 4.4375q0 0.546875 0.0625 0.71875q0.078125 0.15625 0.21875 0.25q0.15625 0.078125 0.453125 0.078125q0.203125 0 0.5625 -0.046875zm0.77508545 -2.640625q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm12.260498 1.015625l1.265625 0.15625q-0.203125 1.3125 -1.0625 2.0625q-0.84375 0.734375 -2.09375 0.734375q-1.5625 0 -2.515625 -1.015625q-0.9375 -1.03125 -0.9375 -2.921875q0 -1.234375 0.40625 -2.15625q0.40625 -0.921875 1.234375 -1.375q0.84375 -0.46875 1.8125 -0.46875q1.25 0 2.03125 0.625q0.78125 0.625 1.015625 1.765625l-1.265625 0.203125q-0.171875 -0.765625 -0.625 -1.15625q-0.453125 -0.390625 -1.09375 -0.390625q-0.984375 0 -1.59375 0.703125q-0.609375 0.703125 -0.609375 2.203125q0 1.53125 0.578125 2.234375q0.59375 0.6875 1.546875 0.6875q0.75 0 1.265625 -0.453125q0.515625 -0.46875 0.640625 -1.4375zm1.890625 -1.015625q0 -2.109375 1.171875 -3.125q0.984375 -0.84375 2.390625 -0.84375q1.578125 0 2.5625 1.03125q1.0 1.015625 1.0 2.828125q0 1.46875 -0.4375 2.3125q-0.4375 0.828125 -1.28125 1.296875q-0.84375 0.46875 -1.84375 0.46875q-1.59375 0 -2.578125 -1.015625q-0.984375 -1.03125 -0.984375 -2.953125zm1.328125 0q0 1.453125 0.625 2.1875q0.640625 0.71875 1.609375 0.71875q0.96875 0 1.59375 -0.71875q0.640625 -0.734375 0.640625 -2.234375q0 -1.40625 -0.640625 -2.125q-0.640625 -0.734375 -1.59375 -0.734375q-0.96875 0 -1.609375 0.71875q-0.625 0.71875 -0.625 2.1875zm7.2760925 3.796875l0 -10.484375l1.28125 0l0 10.484375l-1.28125 0zm2.771759 -2.265625l1.265625 -0.203125q0.109375 0.765625 0.59375 1.171875q0.5 0.40625 1.375 0.40625q0.890625 0 1.3125 -0.359375q0.4375 -0.359375 0.4375 -0.84375q0 -0.4375 -0.375 -0.6875q-0.265625 -0.171875 -1.3125 -0.4375q-1.421875 -0.359375 -1.96875 -0.609375q-0.546875 -0.265625 -0.828125 -0.734375q-0.28125 -0.46875 -0.28125 -1.015625q0 -0.515625 0.21875 -0.9375q0.234375 -0.4375 0.640625 -0.734375q0.296875 -0.21875 0.8125 -0.359375q0.53125 -0.15625 1.125 -0.15625q0.890625 0 1.5625 0.265625q0.671875 0.25 1.0 0.6875q0.328125 0.4375 0.4375 1.171875l-1.25 0.171875q-0.09375 -0.578125 -0.5 -0.90625q-0.40625 -0.34375 -1.15625 -0.34375q-0.890625 0 -1.28125 0.296875q-0.375 0.296875 -0.375 0.6875q0 0.25 0.15625 0.453125q0.15625 0.203125 0.5 0.34375q0.1875 0.078125 1.140625 0.328125q1.359375 0.359375 1.890625 0.59375q0.546875 0.234375 0.859375 0.6875q0.3125 0.4375 0.3125 1.09375q0 0.640625 -0.375 1.21875q-0.375 0.5625 -1.09375 0.875q-0.703125 0.3125 -1.59375 0.3125q-1.484375 0 -2.265625 -0.609375q-0.765625 -0.625 -0.984375 -1.828125z" fill-rule="nonzero"/></g></svg> diff --git a/docs/mkdocs/src/index.md b/docs/mkdocs/src/index.md index a6bf2ae..c81f7ff 100644 --- a/docs/mkdocs/src/index.md +++ b/docs/mkdocs/src/index.md @@ -8,8 +8,7 @@ The project initially only supported BLE (Bluetooth Low Energy), but support for eventually added. Support for BLE is therefore currently somewhat more advanced than for Classic. !!! warning - This project is still very much experimental and in an alpha state where a lot of things are still missing or broken, and what's there changes frequently. - Also, there are still a few hardcoded values/parameters in some of the examples and apps which need to be changed (those will eventually be command line arguments, as appropriate) + This project is still in an early state of development where some things are still missing or broken, and what's implemented may change and evolve frequently. Overview -------- @@ -163,4 +162,3 @@ Future features to be considered include: * Bindings for languages other than Python * RPC interface to expose most of the API for remote use * (...suggest anything you want...) - diff --git a/docs/mkdocs/src/platforms/android.md b/docs/mkdocs/src/platforms/android.md index 872edc7..4b08d88 100644 --- a/docs/mkdocs/src/platforms/android.md +++ b/docs/mkdocs/src/platforms/android.md @@ -2,30 +2,31 @@ =================================== Using Bumble with Android is not about running the Bumble stack on the Android -OS itself, but rather using Bumble with the Bluetooth support of the Android +OS itself, but rather using Bumble with the Bluetooth support of the Android emulator. The two main use cases are: * Connecting the Bumble host stack to the Android emulator's virtual controller. - * Using Bumble as an HCI bridge to connect the Android emulator to a physical - Bluetooth controller, such as a USB dongle + * Using Bumble as an HCI bridge to connect the Android emulator to a physical + Bluetooth controller, such as a USB dongle, or other HCI transport. !!! warning Bluetooth support in the Android emulator is a recent feature that may still be evolving. The information contained here be somewhat out of sync with the version of the emulator you are using. - You will need version 31.3.8.0 or later. + You will need version 33.1.4.0 or later. -The Android emulator supports Bluetooth in two ways: either by exposing virtual +The Android emulator supports Bluetooth in two ways: either by exposing virtual Bluetooth controllers to which you can connect a virtual Bluetooth host stack, or -by exposing an way to connect your own virtual controller to the Android Bluetooth +by exposing a way to connect your own virtual controller to the Android Bluetooth stack via a virtual HCI interface. -Both ways are controlled via gRPC requests to the Android emulator. +Both ways are controlled via gRPC requests to the Android emulator controller and/or +from the Android emulator. ## Launching the Emulator -If the version of the emulator you are running does not yet support enabling +If the version of the emulator you are running does not yet support enabling Bluetooth support by default or automatically, you must launch the emulator from the command line. @@ -33,52 +34,86 @@ the command line. For details on how to launch the Android emulator from the command line, visit [this Android Studio user guide page](https://developer.android.com/studio/run/emulator-commandline) -The `-grpc <port>` command line option may be used to select a gRPC port other than the default. +The `-packet-streamer-endpoint <endpoint>` command line option may be used to enable +Bluetooth emulation and tell the emulator which virtual controller to connect to. -## Connecting to Root Canal +## Connecting to Netsim -The Android emulator's virtual Bluetooth controller is called **Root Canal**. -Multiple instances of Root Canal virtual controllers can be instantiated, they -communicate link layer packets between them, thus creating a virtual radio network. -Configuring a Bumble Device instance to use Root Canal as a virtual controller +If the emulator doesn't have Bluetooth emulation enabled by default, use the +`-packet-streamer-endpoint default` option to tell it to connect to Netsim. +If Netsim is not running, the emulator will start it automatically. + +The Android emulator's virtual Bluetooth controller is called **Netsim**. +Netsim runs as a background process and allows multiple clients to connect to it, +each connecting to its own virtual controller instance hosted by Netsim. All the +clients connected to the same Netsim process can then "talk" to each other over a +virtual radio link layer. +Netsim supports other wireless protocols than Bluetooth, but the relevant part here +is Bluetooth. The virtual Bluetooth controller used by Netsim is sometimes referred to +as **Root Canal**. + +Configuring a Bumble Device instance to use netsim as a virtual controller allows that virtual device to communicate with the Android Bluetooth stack, and through it with Android applications as well as system-managed profiles. -To connect a Bumble host stack to a Root Canal virtual controller instance, use -the bumble `android-emulator` transport in `host` mode (the default). +To connect a Bumble host stack to a netsim virtual controller instance, use +the Bumble `android-netsim` transport in `host` mode (the default). + +!!! example "Run the example GATT server connected to the emulator via Netsim" + ``` shell + $ python run_gatt_server.py device1.json android-netsim + ``` -!!! example "Run the example GATT server connected to the emulator" +By default, the Bumble `android-netsim` transport will try to automatically discover +the port number on which the netsim process is exposing its gRPC server interface. If +that discovery process fails, or if you want to specify the interface manually, you +can pass a `hostname` and `port` as parameters to the transport, as: `android-netsim:<host>:<port>`. + +!!! example "Run the example GATT server connected to the emulator via Netsim on a localhost, port 8877" ``` shell - $ python run_gatt_server.py device1.json android-emulator + $ python run_gatt_server.py device1.json android-netsim:localhost:8877 ``` +### Multiple Instances + +If you want to connect multiple Bumble devices to netsim, it may be useful to give each one +a netsim controller with a specific name. This can be done using the `name=<name>` transport option. +For example: `android-netsim:localhost:8877,name=bumble1` + ## Connecting a Custom Virtual Controller This is an advanced use case, which may not be officially supported, but should work in recent versions of the emulator. -You will likely need to start the emulator from the command line, in order to specify the `-forward-vhci` option (unless the emulator offers a way to control that feature from a user/ui menu). -!!! example "Launch the emulator with VHCI forwarding" - In this example, we launch an emulator AVD named "Tiramisu" +The first step is to run the Bumble HCI bridge, specifying netsim as the "host" end of the +bridge, and another controller (typically a USB Bluetooth dongle, but any other supported +transport can work as well) as the "controller" end of the bridge. + +To connect a virtual controller to the Android Bluetooth stack, use the bumble `android-netsim` transport in `controller` mode. For example, with port number 8877, the transport name would be: `android-netsim:_:8877,mode=controller`. + +!!! example "Connect the Android emulator to the first USB Bluetooth dongle, using the `hci_bridge` application" ```shell - $ emulator -forward-vhci -avd Tiramisu + $ bumble-hci-bridge android-netsim:_:8877,mode=controller usb:0 ``` -!!! tip - Attaching a virtual controller use the VHCI forwarder while the Android Bluetooth stack - is running isn't supported. So you need to disable Bluetooth in your running Android guest - before attaching the virtual controller, then re-enable it once attached. +Then, you can start the emulator and tell it to connect to this bridge, instead of netsim. +You will likely need to start the emulator from the command line, in order to specify the `-packet-streamer-endpoint <hostname>:<port>` option (unless the emulator offers a way to control that feature from a user/ui menu). -To connect a virtual controller to the Android Bluetooth stack, use the bumble `android-emulator` transport in `controller` mode. For example, using the default gRPC port, the transport name would be: `android-emulator:mode=controller`. - -!!! example "Connect the Android emulator to the first USB Bluetooth dongle, using the `hci_bridge` application" +!!! example "Launch the emulator with a netsim replacement" + In this example, we launch an emulator AVD named "Tiramisu", with a Bumble HCI bridge running + on port 8877. ```shell - $ bumble-hci-bridge android-emulator:mode=controller usb:0 + $ emulator -packet-streamer-endpoint localhost:8877 -avd Tiramisu ``` +!!! tip + Attaching a virtual controller while the Android Bluetooth stack is running may not be well supported. So you may need to disable Bluetooth in your running Android guest + before attaching the virtual controller, then re-enable it once attached. + + ## Other Tools The `show` application that's included with Bumble can be used to parse and pretty-print the HCI packets -from an Android HCI "snoop log" (see [this page](https://source.android.com/devices/bluetooth/verifying_debugging) +from an Android HCI "snoop log" (see [this page](https://source.android.com/devices/bluetooth/verifying_debugging) for details on how to obtain HCI snoop logs from an Android device). Use the `--format snoop` option to specify that the file is in that specific format. @@ -86,4 +121,3 @@ Use the `--format snoop` option to specify that the file is in that specific for ```shell $ bumble-show --format snoop btsnoop_hci.log ``` - diff --git a/docs/mkdocs/src/platforms/linux.md b/docs/mkdocs/src/platforms/linux.md index 6468cd9..77f46f8 100644 --- a/docs/mkdocs/src/platforms/linux.md +++ b/docs/mkdocs/src/platforms/linux.md @@ -1,47 +1,85 @@ :material-linux: LINUX PLATFORM =============================== -In addition to all the standard functionality available from the project by running the python tools and/or writing your own apps by leveraging the API, it is also possible on Linux hosts to interface the Bumble stack with the native BlueZ stack, and with Bluetooth controllers. - -Using Bumble With BlueZ ------------------------ +Using Bumble With Physical Bluetooth Controllers +------------------------------------------------ -A Bumble virtual controller can be attached to the BlueZ stack. -Attaching a controller to BlueZ can be done by either simulating a UART HCI interface, or by using the VHCI driver interface if available. -In both cases, the controller can run locally on the Linux host, or remotely on a different host, with a bridge between the remote controller and the local BlueZ host, which may be useful when the BlueZ stack is running on an embedded system, or a host on which running the Bumble controller is not convenient. +A Bumble application can interface with a local Bluetooth controller on a Linux host. +The 3 main types of physical Bluetooth controllers are: -### Using VHCI + * Bluetooth USB Dongle + * HCI over UART (via a serial port) + * Kernel-managed Bluetooth HCI (HCI Sockets) -With the [VHCI transport](../transports/vhci.md) you can attach a Bumble virtual controller to the BlueZ stack. Once attached, the controller will appear just like any other controller, and thus can be used with the standard BlueZ tools. +!!! tip "Conflicts with the kernel and BlueZ" + If your use a USB dongle that is recognized by your kernel as a supported Bluetooth device, it is + likely that the kernel driver will claim that USB device and attach it to the BlueZ stack. + If you want to claim ownership of it to use with Bumble, you will need to set the state of the corresponding HCI interface as `DOWN`. + HCI interfaces are numbered, starting from 0 (i.e `hci0`, `hci1`, ...). -!!! example "Attaching a virtual controller" - With the example app `run_controller.py`: + For example, to bring `hci0` down: ``` - PYTHONPATH=. python3 examples/run_controller.py F6:F7:F8:F9:FA:FB examples/device1.json vhci - ``` - - You should see a 'Virtual Bus' controller. For example: - ``` - $ hciconfig - hci0: Type: Primary Bus: Virtual - BD Address: F6:F7:F8:F9:FA:FB ACL MTU: 27:64 SCO MTU: 0:0 - UP RUNNING - RX bytes:0 acl:0 sco:0 events:43 errors:0 - TX bytes:274 acl:0 sco:0 commands:43 errors:0 + $ sudo hciconfig hci0 down ``` - And scanning for devices should show the virtual 'Bumble' device that's running as part of the `run_controller.py` example app: + You can use the `hciconfig` command with no arguments to get a list of HCI interfaces seen by + the kernel. + + Also, if `bluetoothd` is running on your system, it will likely re-claim the interface after you + close it, so you may need to bring the interface back `UP` before using it again, or to disable + `bluetoothd` altogether (see the section further below about BlueZ and `bluetoothd`). + +### Using a USB Dongle + +See the [USB Transport page](../transports/usb.md) for general information on how to use HCI USB controllers. + +!!! tip "USB Permissions" + By default, when running as a regular user, you won't have the permission to use + arbitrary USB devices. + You can change the permissions for a specific USB device based on its bus number and + device number (you can use `lsusb` to find the Bus and Device numbers for your Bluetooth + dongle). + + Example: ``` - pi@raspberrypi:~ $ sudo hcitool -i hci2 lescan - LE Scan ... - F0:F1:F2:F3:F4:F5 Bumble + $ sudo chmod o+w /dev/bus/usb/001/004 ``` + This will change the permissions for Device 4 on Bus 1. + + Note that the USB Bus number and Device number may change depending on where you plug the USB + dongle and what other USB devices and hubs are also plugged in. + + If you need to make the permission changes permanent across reboots, you can create a `udev` + rule for your specific Bluetooth dongle. Visit [this Arch Linux Wiki page](https://wiki.archlinux.org/title/udev) for a + good overview of how you may do that. + +### Using HCI over UART + +See the [Serial Transport page](../transports/serial.md) for general information on how to use HCI over a UART (serial port). ### Using HCI Sockets HCI sockets provide a way to send/receive HCI packets to/from a Bluetooth controller managed by the kernel. -The HCI device referenced by an `hci-socket` transport (`hciX`, where `X` is an integer, with `hci0` being the first controller device, and so on) must be in the `DOWN` state before it can be opened as a transport. -You can bring a HCI controller `UP` or `DOWN` with `hciconfig`. +See the [HCI Socket Transport page](../transports/hci_socket.md) for details on the `hci-socket` tansport syntax. + +The HCI device referenced by an `hci-socket` transport (`hci<X>`, where `<X>` is an integer, with `hci0` being the first controller device, and so on) must be in the `DOWN` state before it can be opened as a transport. +You can bring a HCI controller `UP` or `DOWN` with `hciconfig hci<X> up` and `hciconfig hci<X> up`. + +!!! tip "HCI Socket Permissions" + By default, when running as a regular user, you won't have the permission to use + an HCI socket to a Bluetooth controller (you may see an exception like `PermissionError: [Errno 1] Operation not permitted`). + + If you want to run without using `sudo`, you need to manage the capabilities by adding the appropriate entries in `/etc/security/capability.conf` to grant a user or group the `cap_net_admin` capability. + See [this manpage](https://manpages.ubuntu.com/manpages/bionic/man5/capability.conf.5.html) for details. + + Alternatively, if you are just experimenting temporarily, the `capsh` command may be useful in order + to execute a single command with enhanced permissions, as in this example: + + + ``` + $ sudo capsh --caps="cap_net_admin+eip cap_setpcap,cap_setuid,cap_setgid+ep" --keep=1 --user=$USER --addamb=cap_net_admin -- -c "<path/to/executable> <executable-args>" + ``` + Where `<path/to/executable>` is the path to your `python3` executable or to one of the Bumble bundled command-line applications. !!! tip "List all available controllers" The command @@ -56,45 +94,32 @@ You can bring a HCI controller `UP` or `DOWN` with `hciconfig`. pi@raspberrypi:~ $ hciconfig hci1: Type: Primary Bus: USB BD Address: 00:16:A4:5A:40:F2 ACL MTU: 1021:8 SCO MTU: 64:1 - DOWN + DOWN RX bytes:84056 acl:0 sco:0 events:51 errors:0 TX bytes:1980 acl:0 sco:0 commands:90 errors:0 hci0: Type: Primary Bus: UART BD Address: DC:A6:32:75:2C:97 ACL MTU: 1021:8 SCO MTU: 64:1 - DOWN + DOWN RX bytes:68038 acl:0 sco:0 events:692 errors:0 TX bytes:20105 acl:0 sco:0 commands:843 errors:0 ``` !!! tip "Disabling `bluetoothd`" - When the Bluetooth daemon, `bluetoothd`, is running, it will try to use any HCI controller attached to the BlueZ stack, automatically. This means that whenever an HCI socket transport is released, it is likely that `bluetoothd` will take it over, so you will get a "device busy" condition (ex: `OSError: [Errno 16] Device or resource busy`). If that happens, you can always use + When the Bluetooth daemon, `bluetoothd`, is running, it will try to use any HCI controller attached to the BlueZ stack, automatically. This means that whenever an HCI socket transport is released, it is likely that `bluetoothd` will take it over, so you will get a "device busy" condition (ex: `OSError: [Errno 16] Device or resource busy`). If that happens, you can always use ``` $ hciconfig hci0 down - ``` - (or `hciX` with `X` being the index of the controller device you want to use), but a simpler solution is to just stop the `bluetoothd` daemon, with a command like: + ``` + (or `hci<X>` with `<X>` being the index of the controller device you want to use), but a simpler solution is to just stop the `bluetoothd` daemon, with a command like: ``` $ sudo systemctl stop bluetooth.service ``` You can always re-start the daemon with ``` $ sudo systemctl start bluetooth.service - ``` -### Using a Simulated UART HCI - -### Bridge to a Remote Controller - - -Using Bumble With Bluetooth Controllers ---------------------------------------- - -A Bumble application can interface with a local Bluetooth controller. -If your Bluetooth controller is a standard HCI USB controller, see the [USB Transport page](../transports/usb.md) for details on how to use HCI USB controllers. -If your Bluetooth controller is a standard HCI UART controller, see the [Serial Transport page](../transports/serial.md). -Alternatively, a Bumble Host object can communicate with one of the platform's controllers via an HCI Socket. - -`<details to be filled in>` +Bumble on the Raspberry Pi +-------------------------- ### Raspberry Pi 4 :fontawesome-brands-raspberry-pi: @@ -102,9 +127,10 @@ You can use the Bluetooth controller either via the kernel, or directly to the d #### Via The Kernel -Use an HCI Socket transport +Use an HCI Socket transport (see section above) #### Directly + In order to use the Bluetooth controller directly on a Raspberry Pi 4 board, you need to ensure that it isn't being used by the BlueZ stack (which it probably is by default). ``` @@ -136,3 +162,45 @@ should detach the controller from the stack, after which you can use the HCI UAR python3 run_scanner.py serial:/dev/serial1,3000000 ``` + +Using Bumble With BlueZ +----------------------- + +In addition to all the standard functionality available from the project by running the python tools and/or writing your own apps by leveraging the API, it is also possible on Linux hosts to interface the Bumble stack with the native BlueZ stack, and with Bluetooth controllers. + +A Bumble virtual controller can be attached to the BlueZ stack. +Attaching a controller to BlueZ can be done by either simulating a UART HCI interface, or by using the VHCI driver interface if available. +In both cases, the controller can run locally on the Linux host, or remotely on a different host, with a bridge between the remote controller and the local BlueZ host, which may be useful when the BlueZ stack is running on an embedded system, or a host on which running the Bumble controller is not convenient. + +### Using VHCI + +With the [VHCI transport](../transports/vhci.md) you can attach a Bumble virtual controller to the BlueZ stack. Once attached, the controller will appear just like any other controller, and thus can be used with the standard BlueZ tools. + +!!! example "Attaching a virtual controller" + With the example app `run_controller.py`: + ``` + python3 examples/run_controller.py F6:F7:F8:F9:FA:FB examples/device1.json vhci + ``` + + You should see a 'Virtual Bus' controller. For example: + ``` + $ hciconfig + hci0: Type: Primary Bus: Virtual + BD Address: F6:F7:F8:F9:FA:FB ACL MTU: 27:64 SCO MTU: 0:0 + UP RUNNING + RX bytes:0 acl:0 sco:0 events:43 errors:0 + TX bytes:274 acl:0 sco:0 commands:43 errors:0 + ``` + + And scanning for devices should show the virtual 'Bumble' device that's running as part of the `run_controller.py` example app: + ``` + pi@raspberrypi:~ $ sudo hcitool -i hci2 lescan + LE Scan ... + F0:F1:F2:F3:F4:F5 Bumble + ``` + + ``` + +### Using a Simulated UART HCI + +### Bridge to a Remote Controller diff --git a/docs/mkdocs/src/platforms/macos.md b/docs/mkdocs/src/platforms/macos.md index 7c5bc32..2133ad6 100644 --- a/docs/mkdocs/src/platforms/macos.md +++ b/docs/mkdocs/src/platforms/macos.md @@ -11,4 +11,3 @@ To do that, use the following command: sudo nvram bluetoothHostControllerSwitchBehavior="never" ``` A reboot shouldn't be necessary after that. See [Tech Note 2295](https://developer.apple.com/library/archive/technotes/tn2295/_index.html) - diff --git a/docs/mkdocs/src/platforms/windows.md b/docs/mkdocs/src/platforms/windows.md index 2157695..330f1ab 100644 --- a/docs/mkdocs/src/platforms/windows.md +++ b/docs/mkdocs/src/platforms/windows.md @@ -7,7 +7,7 @@ USB HCI To use a Bluetooth USB dongle on Windows, you need a USB dongle that does not require a vendor Windows driver (the dongle will be used directly through the [`WinUSB`](https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/winusb) driver rather than through a vendor-supplied Windows driver). In order to use the dongle, the `WinUSB` driver must be assigned to the USB device. It is likely that, by default, when you first plug in the dongle, it will be recognized by Windows as a Bluetooth USB device, and Windows will try to use it with its native Bluetooth stack. You will need to switch the driver, which can be done easily with the [Zadig tool](https://zadig.akeo.ie/). -In the Zadig tool, select your USB dongle device, and associate it with WinUSB. +In the Zadig tool, select your USB dongle device, and associate it with WinUSB. Once the WinUSB driver is correctly assigned to your device, you can confirm that by checking the settings with the Windows Device Manager control panel. Your device should appear under "Universal Serial Bus Device" (not under "Bluetooth"), and inspecting the driver details, you should see `winusb.sys` in the list of driver files. ![USB Driver Details](winusb_driver.png) diff --git a/docs/mkdocs/src/transports/android_emulator.md b/docs/mkdocs/src/transports/android_emulator.md index d903394..974ba4f 100644 --- a/docs/mkdocs/src/transports/android_emulator.md +++ b/docs/mkdocs/src/transports/android_emulator.md @@ -1,21 +1,41 @@ ANDROID EMULATOR TRANSPORT ========================== -The Android emulator transport either connects, as a host, to a "Root Canal" virtual controller -("host" mode), or attaches a virtual controller to the Android Bluetooth host stack ("controller" mode). +!!! warning + Bluetooth support in the Android emulator has recently changed. The older mode, using + the `android-emulator` transport name with Bumble, while still implemented, is now + obsolete, and may not be supported by recent versions of the emulator. + Use the `android-netsim` transport name instead. + + +The Android "netsim" transport either connects, as a host, to a **Netsim** virtual controller +("host" mode), or acts as a virtual controller itself ("controller" mode) accepting host +connections. ## Moniker -The moniker syntax for an Android Emulator transport is: `android-emulator:[mode=<host|controller>][mode=<host|controller>]`. -Both the `mode=<host|controller>` and `mode=<host|controller>` parameters are optional (so the moniker `android-emulator` by itself is a valid moniker, which will create a transport in `host` mode, connected to `localhost` on the default gRPC port for the emulator) +The moniker syntax for an Android Emulator "netsim" transport is: `android-netsim:[<host>:<port>][<options>]`, +where `<options>` is a ','-separated list of `<name>=<value>` pairs`. +The `mode` parameter name can specify running as a host or a controller, and `<hostname>:<port>` can specify a host name (or IP address) and TCP port number on which to reach the gRPC server for the emulator (in "host" mode), or to accept gRPC connections (in "controller" mode). +Both the `mode=<host|controller>` and `<hostname>:<port>` parameters are optional (so the moniker `android-netsim` by itself is a valid moniker, which will create a transport in `host` mode, connected to `localhost` on the default gRPC port for the Netsim background process). + +!!! example Example + `android-netsim` + connect as a host to Netsim on the gRPC port discovered automatically. !!! example Example - `android-emulator` - connect as a host to the emulator on localhost:8554 + `android-netsim:_:8555,mode=controller` + Run as a controller, accepting gRPC connection on port 8555. !!! example Example - `android-emulator:mode=controller` - connect as a controller to the emulator on localhost:8554 + `android-netsim:localhost:8555` + connect as a host to Netsim on localhost:8555 !!! example Example - `android-emulator:localhost:8555` - connect as a host to the emulator on localhost:8555 + `android-netsim:localhost:8555` + connect as a host to Netsim on localhost:8555 + +!!! example Example + `android-netsim:name=bumble1234` + connect as a host to Netsim on the discovered gRPC port, using `bumble1234` as the + controller instance name. + diff --git a/docs/mkdocs/src/transports/file.md b/docs/mkdocs/src/transports/file.md index d20154f..b283137 100644 --- a/docs/mkdocs/src/transports/file.md +++ b/docs/mkdocs/src/transports/file.md @@ -8,5 +8,5 @@ This is typically used to open a PTY, or unix driver, not for real files. The moniker for a File transport is `file:<path>` !!! example - `file:/dev/ttys001` - Opens the pseudo terminal `/dev/ttys001` as a transport
\ No newline at end of file + `file:/dev/ttys001` + Opens the pseudo terminal `/dev/ttys001` as a transport diff --git a/docs/mkdocs/src/transports/hci_socket.md b/docs/mkdocs/src/transports/hci_socket.md index 58cdbfb..c79de36 100644 --- a/docs/mkdocs/src/transports/hci_socket.md +++ b/docs/mkdocs/src/transports/hci_socket.md @@ -10,8 +10,8 @@ An HCI Socket can send/receive HCI packets to/from a Bluetooth HCI controller ma The moniker for an HCI Socket transport is either just `hci-socket` (to use the default/first Bluetooth controller), or `hci-socket:<index>` where `<index>` is the 0-based index of a Bluetooth controller device. !!! example - `hci-socket` + `hci-socket` Use an HCI socket to the first Bluetooth controller (`hci0 on Linux`) !!! tip "On Linux" - See the [Linux Platform](../platforms/linux.md) page for details on how to use HCI sockets on Linux
\ No newline at end of file + See the [Linux Platform](../platforms/linux.md) page for details on how to use HCI sockets on Linux diff --git a/docs/mkdocs/src/transports/index.md b/docs/mkdocs/src/transports/index.md index 70827c1..feb57e5 100644 --- a/docs/mkdocs/src/transports/index.md +++ b/docs/mkdocs/src/transports/index.md @@ -16,5 +16,6 @@ Several types of transports are supported: * [PTY](pty.md): a PTY (pseudo terminal) is used to send/receive HCI packets. This is convenient to expose a virtual controller as if it were an HCI UART * [VHCI](vhci.md): used to attach a virtual controller to a Bluetooth stack on platforms that support it. * [HCI Socket](hci_socket.md): an HCI socket, on platforms that support it, to send/receive HCI packets to/from an HCI controller managed by the OS. - * [Android Emulator](android_emulator.md): a gRPC connection to an Android emulator is used to setup either an HCI interface to the emulator's "Root Canal" virtual controller, or attach a virtual controller to the Android Bluetooth host stack. + * [Android Emulator](android_emulator.md): a gRPC connection to the Android emulator's "netsim" + virtual controller, or from the Android emulator, is used to setup either an HCI interface to the emulator's "netsim" virtual controller, or serve as a virtual controller for the Android Bluetooth host stack. * [File](file.md): HCI packets are read/written to a file-like node in the filesystem. diff --git a/docs/mkdocs/src/transports/pty.md b/docs/mkdocs/src/transports/pty.md index fafa3c9..2c85050 100644 --- a/docs/mkdocs/src/transports/pty.md +++ b/docs/mkdocs/src/transports/pty.md @@ -8,5 +8,5 @@ The moniker syntax for a PTY transport is: `pty[:path]`. Where `path`, is used, is the path name where a symbolic link to the PTY will be created for convenience (the link will be removed when the transport is closed or when the process exits). !!! example - `pty:virtual_hci` + `pty:virtual_hci` Creates a PTY entry and a symbolic link, named `virtual_hci`, linking to the PTY diff --git a/docs/mkdocs/src/transports/serial.md b/docs/mkdocs/src/transports/serial.md index 3ce2e38..acc2c0d 100644 --- a/docs/mkdocs/src/transports/serial.md +++ b/docs/mkdocs/src/transports/serial.md @@ -8,5 +8,5 @@ The moniker syntax for a serial transport is: `serial:<device-path>[,<speed>]` When `<speed>` is omitted, the default value of 1000000 is used !!! example - `serial:/dev/tty.usbmodem0006839912172,1000000` + `serial:/dev/tty.usbmodem0006839912172,1000000` Opens the serial port `/dev/tty.usbmodem0006839912172` at `1000000`bps diff --git a/docs/mkdocs/src/transports/tcp_client.md b/docs/mkdocs/src/transports/tcp_client.md index 39fb477..a5a4154 100644 --- a/docs/mkdocs/src/transports/tcp_client.md +++ b/docs/mkdocs/src/transports/tcp_client.md @@ -7,5 +7,5 @@ The TCP Client transport uses an outgoing TCP connection to a host:port address. The moniker syntax for a TCP client transport is: `tcp-client:<remote-host>:<remote-port>` !!! example - `tcp-client:127.0.0.1:9001` - Connects to port 9001 on the local host
\ No newline at end of file + `tcp-client:127.0.0.1:9001` + Connects to port 9001 on the local host diff --git a/docs/mkdocs/src/transports/tcp_server.md b/docs/mkdocs/src/transports/tcp_server.md index 3af8ff0..966308e 100644 --- a/docs/mkdocs/src/transports/tcp_server.md +++ b/docs/mkdocs/src/transports/tcp_server.md @@ -5,9 +5,9 @@ The TCP Client transport uses an incoming TCP connection to a host:port address. ## Moniker The moniker syntax for a TCP server transport is: `tcp-server:<local-host>:<local-port>` -where `<local-host>` may be the address of a local network interface, or `_` to accept +where `<local-host>` may be the address of a local network interface, or `_` to accept connections on all local network interfaces. !!! example - `tcp-server:_:9001` - Waits for and accepts connections on port `9001`
\ No newline at end of file + `tcp-server:_:9001` + Waits for and accepts connections on port `9001` diff --git a/docs/mkdocs/src/transports/udp.md b/docs/mkdocs/src/transports/udp.md index 4238bc8..ad9c245 100644 --- a/docs/mkdocs/src/transports/udp.md +++ b/docs/mkdocs/src/transports/udp.md @@ -7,5 +7,5 @@ The UDP transport is a UDP socket, receiving packets on a specified port number, The moniker syntax for a UDP transport is: `udp:<local-host>:<local-port>,<remote-host>:<remote-port>`. !!! example - `udp:0.0.0.0:9000,127.0.0.1:9001` + `udp:0.0.0.0:9000,127.0.0.1:9001` UDP transport where packets are received on port `9000` and sent to `127.0.0.1` on port `9001` diff --git a/docs/mkdocs/src/transports/usb.md b/docs/mkdocs/src/transports/usb.md index f35259b..e400630 100644 --- a/docs/mkdocs/src/transports/usb.md +++ b/docs/mkdocs/src/transports/usb.md @@ -4,16 +4,70 @@ USB TRANSPORT The USB transport interfaces with a local Bluetooth USB dongle. ## Moniker -The moniker for a USB transport is either `usb:<index>` or `usb:<vendor>:<product>` -with `<index>` as the 0-based index to select amongst all the devices that appear to be supporting Bluetooth HCI (0 being the first one), or where `<vendor>` and `<product>` are a vendor ID and product ID in hexadecimal. +The moniker for a USB transport is either: -!!! example - `usb:04b4:f901` - Use the USB dongle with `vendor` equal to `04b4` and `product` equal to `f901` + * `usb:<index>` + * `usb:<vendor>:<product>` + * `usb:<vendor>:<product>/<serial-number>` + * `usb:<vendor>:<product>#<index>` + +with `<index>` as a 0-based index (0 being the first one) to select amongst all the matching devices when there are more than one. +In the `usb:<index>` form, matching devices are the ones supporting Bluetooth HCI, as declared by their Class, Subclass and Protocol. +In the `usb:<vendor>:<product>#<index>` form, matching devices are the ones with the specified `<vendor>` and `<product>` identification. + +`<vendor>` and `<product>` are a vendor ID and product ID in hexadecimal. + +In addition, if the moniker ends with the symbol "!", the device will be used in "forced" mode: +the first USB interface of the device will be used, regardless of the interface class/subclass. +This may be useful for some devices that use a custom class/subclass but may nonetheless work as-is. + +!!! examples + `usb:04b4:f901` + The USB dongle with `<vendor>` equal to `04b4` and `<product>` equal to `f901` + + `usb:0` + The first Bluetooth HCI dongle that's declared as such by Class/Subclass/Protocol + + `usb:04b4:f901/0016A45B05D8` + The USB dongle with `<vendor>` equal to `04b4`, `<product>` equal to `f901` and `<serial>` equal to `0016A45B05D8` + + `usb:04b4:f901/#1` + The second USB dongle with `<vendor>` equal to `04b4` and `<product>` equal to `f901` + + `usb:0B05:17CB!` + The BT USB dongle vendor=0B05 and product=17CB, in "forced" mode. - `usb:0` - Use the first Bluetooth dongle ## Alternative The library includes two different implementations of the USB transport, implemented using different python bindings for `libusb`. Using the transport prefix `pyusb:` instead of `usb:` selects the implementation based on [PyUSB](https://pypi.org/project/pyusb/), using the synchronous API of `libusb`, whereas the default implementation is based on [libusb1](https://pypi.org/project/libusb1/), using the asynchronous API of `libusb`. In order to use the alternative PyUSB-based implementation, you need to ensure that you have installed that python module, as it isn't installed by default as a dependency of Bumble. + +## Libusb + +The `libusb-1.0` shared library is required to use both `usb` and `pyusb` transports. This library should be installed automatically with Bumble, as part of the `libusb_package` Python package. +If your OS or architecture is not supported by `libusb_package`, you can install a system-wide library with `brew install libusb` for Mac or `apt install libusb-1.0-0` for Linux. + +## Listing Available USB Devices + +### With `usb_probe` +You can use the [`usb_probe`](../apps_and_tools/usb_probe.md) tool to list all the USB devices attached to your host computer. +The tool will also show the `usb:XXX` transport name(s) you can use to reference each device. + + +### With `lsusb` +On Linux and macOS, the `lsusb` tool serves a similar purpose to Bumble's own `usb_probe` tool (without the Bumble specifics) + +#### Installing lsusb + +On Mac: `brew install lsusb` +On Linux: `sudo apt-get install usbutils` + +#### Using lsusb + +``` +$ lsusb +Bus 004 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub +Bus 003 Device 014: ID 0b05:17cb ASUSTek Computer, Inc. Broadcom BCM20702A0 Bluetooth +``` + +The device id for the Bluetooth interface in this case is `0b05:17cb`. diff --git a/docs/mkdocs/src/transports/vhci.md b/docs/mkdocs/src/transports/vhci.md index 9d967c4..4d192f2 100644 --- a/docs/mkdocs/src/transports/vhci.md +++ b/docs/mkdocs/src/transports/vhci.md @@ -10,5 +10,5 @@ The VHCI transport allows attaching a virtual controller to the Bluetooth stack The moniker for a VHCI transport is either just `vhci` (to use the default VHCI device path at `/dev/vhci`), or `vhci:<path>` where `<path>` is the path of a VHCI device. !!! example - `vhci` - Attaches a virtual controller transport to `/dev/vhci`
\ No newline at end of file + `vhci` + Attaches a virtual controller transport to `/dev/vhci` diff --git a/docs/mkdocs/src/transports/ws_client.md b/docs/mkdocs/src/transports/ws_client.md index 4238bc8..ad9c245 100644 --- a/docs/mkdocs/src/transports/ws_client.md +++ b/docs/mkdocs/src/transports/ws_client.md @@ -7,5 +7,5 @@ The UDP transport is a UDP socket, receiving packets on a specified port number, The moniker syntax for a UDP transport is: `udp:<local-host>:<local-port>,<remote-host>:<remote-port>`. !!! example - `udp:0.0.0.0:9000,127.0.0.1:9001` + `udp:0.0.0.0:9000,127.0.0.1:9001` UDP transport where packets are received on port `9000` and sent to `127.0.0.1` on port `9001` diff --git a/docs/mkdocs/src/transports/ws_server.md b/docs/mkdocs/src/transports/ws_server.md index 4238bc8..ad9c245 100644 --- a/docs/mkdocs/src/transports/ws_server.md +++ b/docs/mkdocs/src/transports/ws_server.md @@ -7,5 +7,5 @@ The UDP transport is a UDP socket, receiving packets on a specified port number, The moniker syntax for a UDP transport is: `udp:<local-host>:<local-port>,<remote-host>:<remote-port>`. !!! example - `udp:0.0.0.0:9000,127.0.0.1:9001` + `udp:0.0.0.0:9000,127.0.0.1:9001` UDP transport where packets are received on port `9000` and sent to `127.0.0.1` on port `9001` diff --git a/docs/mkdocs/src/use_cases/use_case_1.md b/docs/mkdocs/src/use_cases/use_case_1.md index 3b738ac..587122e 100644 --- a/docs/mkdocs/src/use_cases/use_case_1.md +++ b/docs/mkdocs/src/use_cases/use_case_1.md @@ -11,4 +11,4 @@ Write a python application (ex: a GATT client that will connect to a hear rate s | Python || Host |<-- HCI -->| Controller |{...radio...}| Device | | App || | Transport | | | | +--------++--------+ +------------+ +-----------+ -```
\ No newline at end of file +``` diff --git a/docs/mkdocs/src/use_cases/use_case_2.md b/docs/mkdocs/src/use_cases/use_case_2.md index 6875a03..cbcc4f3 100644 --- a/docs/mkdocs/src/use_cases/use_case_2.md +++ b/docs/mkdocs/src/use_cases/use_case_2.md @@ -11,4 +11,4 @@ Connect a native Bluetooth application, running on a host with Bluetooth stack t | Bluetooth |<-- HCI -->| Virtual |<== Local or ==>| Virtual || Host || Python | | App | Transport | Controller | Remote | Controller || || App | +-----------+ +------------+ Link +------------++--------++--------+ -```
\ No newline at end of file +``` diff --git a/docs/mkdocs/src/use_cases/use_case_3.md b/docs/mkdocs/src/use_cases/use_case_3.md index 0cf477d..6336641 100644 --- a/docs/mkdocs/src/use_cases/use_case_3.md +++ b/docs/mkdocs/src/use_cases/use_case_3.md @@ -11,4 +11,4 @@ Connect an emulated Bluetooth device (ex: an Android emulator, or an embedded de | Bluetooth |<-- HCI -->| Virtual |<== Local or ==>| Virtual || Host || Python | | Device | Transport | Controller | Remote | Controller || || App | +-----------+ +------------+ Link +------------++--------++--------+ -```
\ No newline at end of file +``` diff --git a/docs/mkdocs/src/use_cases/use_case_4.md b/docs/mkdocs/src/use_cases/use_case_4.md index 762f974..7bb237f 100644 --- a/docs/mkdocs/src/use_cases/use_case_4.md +++ b/docs/mkdocs/src/use_cases/use_case_4.md @@ -6,9 +6,9 @@ USE CASE 4 Connect two emulated Bluetooth device (ex: an Android emulator, or an embedded device emulator) to each other ``` -+-----------+ +------------+ +------------+ +-----------+ ++-----------+ +------------+ +------------+ +-----------+ | Emulated | | Bumble | Bumble | Bumble | | Emulated | | Bluetooth |<-- HCI -->| Virtual |<== Local or ==>| Virtual |<-- HCI -->| Bluetooth | | Device | Transport | Controller | Remote | Controller | Transport | Device | +-----------+ +------------+ Link +------------+ +-----------+ -```
\ No newline at end of file +``` diff --git a/docs/mkdocs/src/use_cases/use_case_5.md b/docs/mkdocs/src/use_cases/use_case_5.md index 7232105..2c2fc73 100644 --- a/docs/mkdocs/src/use_cases/use_case_5.md +++ b/docs/mkdocs/src/use_cases/use_case_5.md @@ -17,4 +17,4 @@ Write several python applications (ex: a GATT client that will connect to a hear | Python || Host || Controller |<--+ +-->| Controller || Host || Python | | App || || | | || || App | +--------++--------++------------+ +------------++--------++--------+ -```
\ No newline at end of file +``` diff --git a/docs/mkdocs/src/use_cases/use_case_6.md b/docs/mkdocs/src/use_cases/use_case_6.md index f27a184..1e81bcc 100644 --- a/docs/mkdocs/src/use_cases/use_case_6.md +++ b/docs/mkdocs/src/use_cases/use_case_6.md @@ -6,9 +6,9 @@ USE CASE 6 It can be useful to connect an emulated device (like a phone simulator, or an emulated embedded device) to a physical controller in order to connect to other Bluetooth devices. By doing this via a Bumble HCI bridge, it becomes easy to inspect the HCI packets exchanged with the controller, and possibly filter/change them if needed (for example to support vendor-specific HCI extensions). ``` -+-----------+ +--------+ +------------+ +-----------+ ++-----------+ +--------+ +------------+ +-----------+ | Emulated | | Bumble | | Physical | | Bluetooth | | Bluetooth |<-- HCI -->| HCI |<-- HCI -->| Controller |{...radio...}| Device | | Device | Transport | Bridge | Transport | | | | +-----------+ +--------+ +------------+ +-----------+ -```
\ No newline at end of file +``` diff --git a/examples/a2dp_sink1.json b/examples/a2dp_sink1.json index 61ce80d..8603194 100644 --- a/examples/a2dp_sink1.json +++ b/examples/a2dp_sink1.json @@ -1,5 +1,6 @@ { "name": "Bumble Speaker", + "address": "F0:F1:F2:F3:F4:F5", "class_of_device": 2360324, "keystore": "JsonKeyStore" } diff --git a/examples/asha_sink1.json b/examples/asha_sink1.json new file mode 100644 index 0000000..badef8b --- /dev/null +++ b/examples/asha_sink1.json @@ -0,0 +1,5 @@ +{ + "name": "Bumble Aid Left", + "address": "F1:F2:F3:F4:F5:F6", + "keystore": "JsonKeyStore" +} diff --git a/examples/asha_sink2.json b/examples/asha_sink2.json new file mode 100644 index 0000000..785d406 --- /dev/null +++ b/examples/asha_sink2.json @@ -0,0 +1,5 @@ +{ + "name": "Bumble Aid Right", + "address": "F7:F8:F9:FA:FB:FC", + "keystore": "JsonKeyStore" +} diff --git a/examples/async_runner.py b/examples/async_runner.py index d0d1a12..9e71899 100644 --- a/examples/async_runner.py +++ b/examples/async_runner.py @@ -80,6 +80,7 @@ async def main(): await my_work_queue2.run() print("MAIN: end (should never get here)") + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/battery_client.py b/examples/battery_client.py index 888b23e..3cf11b4 100644 --- a/examples/battery_client.py +++ b/examples/battery_client.py @@ -19,8 +19,8 @@ import asyncio import sys import os import logging -from colors import color -from bumble.device import Device, Peer +from bumble.colors import color +from bumble.device import Device from bumble.transport import open_transport from bumble.profiles.battery_service import BatteryServiceProxy @@ -43,30 +43,28 @@ async def main(): # Connect to the peer target_address = sys.argv[2] print(f'=== Connecting to {target_address}...') - connection = await device.connect(target_address) - print(f'=== Connected to {connection}') + async with device.connect_as_gatt(target_address) as peer: + print(f'=== Connected to {peer}') + battery_service = peer.create_service_proxy(BatteryServiceProxy) - # Discover the Battery Service - peer = Peer(connection) - print('=== Discovering Battery Service') - battery_service = await peer.discover_and_create_service_proxy(BatteryServiceProxy) + # Check that the service was found + if not battery_service: + print('!!! Service not found') + return - # Check that the service was found - if not battery_service: - print('!!! Service not found') - return + # Subscribe to and read the battery level + if battery_service.battery_level: + await battery_service.battery_level.subscribe( + lambda value: print( + f'{color("Battery Level Update:", "green")} {value}' + ) + ) + value = await battery_service.battery_level.read_value() + print(f'{color("Initial Battery Level:", "green")} {value}') - # Subscribe to and read the battery level - if battery_service.battery_level: - await battery_service.battery_level.subscribe( - lambda value: print(f'{color("Battery Level Update:", "green")} {value}') - ) - value = await battery_service.battery_level.read_value() - print(f'{color("Initial Battery Level:", "green")} {value}') - - await hci_source.wait_for_termination() + await peer.sustain() # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/battery_server.py b/examples/battery_server.py index 3fabf0d..b7f941f 100644 --- a/examples/battery_server.py +++ b/examples/battery_server.py @@ -38,17 +38,25 @@ async def main(): async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink): device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink) - # Add a Device Information Service and Battery Service to the GATT sever + # Add a Battery Service to the GATT sever battery_service = BatteryService(lambda _: random.randint(0, 100)) device.add_service(battery_service) # Set the advertising data device.advertising_data = bytes( - AdvertisingData([ - (AdvertisingData.COMPLETE_LOCAL_NAME, bytes('Bumble Battery', 'utf-8')), - (AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, bytes(battery_service.uuid)), - (AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)) - ]) + AdvertisingData( + [ + ( + AdvertisingData.COMPLETE_LOCAL_NAME, + bytes('Bumble Battery', 'utf-8'), + ), + ( + AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, + bytes(battery_service.uuid), + ), + (AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)), + ] + ) ) # Go! @@ -58,9 +66,11 @@ async def main(): # Notify every 3 seconds while True: await asyncio.sleep(3.0) - await device.notify_subscribers(battery_service.battery_level_characteristic) + await device.notify_subscribers( + battery_service.battery_level_characteristic + ) # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/device_information_client.py b/examples/device_information_client.py index ed5892b..416aa2f 100644 --- a/examples/device_information_client.py +++ b/examples/device_information_client.py @@ -19,7 +19,7 @@ import asyncio import sys import os import logging -from colors import color +from bumble.colors import color from bumble.device import Device, Peer from bumble.profiles.device_information_service import DeviceInformationServiceProxy from bumble.transport import open_transport @@ -28,7 +28,9 @@ from bumble.transport import open_transport # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) != 3: - print('Usage: device_information_client.py <transport-spec> <bluetooth-address>') + print( + 'Usage: device_information_client.py <transport-spec> <bluetooth-address>' + ) print('example: device_information_client.py usb:0 E1:CA:72:48:C4:E8') return @@ -49,7 +51,9 @@ async def main(): # Discover the Device Information service peer = Peer(connection) print('=== Discovering Device Information Service') - device_information_service = await peer.discover_service_and_create_proxy(DeviceInformationServiceProxy) + device_information_service = await peer.discover_service_and_create_proxy( + DeviceInformationServiceProxy + ) # Check that the service was found if device_information_service is None: @@ -58,23 +62,53 @@ async def main(): # Read and print the fields if device_information_service.manufacturer_name is not None: - print(color('Manufacturer Name: ', 'green'), await device_information_service.manufacturer_name.read_value()) + print( + color('Manufacturer Name: ', 'green'), + await device_information_service.manufacturer_name.read_value(), + ) if device_information_service.model_number is not None: - print(color('Model Number: ', 'green'), await device_information_service.model_number.read_value()) + print( + color('Model Number: ', 'green'), + await device_information_service.model_number.read_value(), + ) if device_information_service.serial_number is not None: - print(color('Serial Number: ', 'green'), await device_information_service.serial_number.read_value()) + print( + color('Serial Number: ', 'green'), + await device_information_service.serial_number.read_value(), + ) if device_information_service.hardware_revision is not None: - print(color('Hardware Revision: ', 'green'), await device_information_service.hardware_revision.read_value()) + print( + color('Hardware Revision: ', 'green'), + await device_information_service.hardware_revision.read_value(), + ) if device_information_service.firmware_revision is not None: - print(color('Firmware Revision: ', 'green'), await device_information_service.firmware_revision.read_value()) + print( + color('Firmware Revision: ', 'green'), + await device_information_service.firmware_revision.read_value(), + ) if device_information_service.software_revision is not None: - print(color('Software Revision: ', 'green'), await device_information_service.software_revision.read_value()) + print( + color('Software Revision: ', 'green'), + await device_information_service.software_revision.read_value(), + ) if device_information_service.system_id is not None: - print(color('System ID: ', 'green'), await device_information_service.system_id.read_value()) - if device_information_service.ieee_regulatory_certification_data_list is not None: - print(color('Regulatory Certification:', 'green'), (await device_information_service.ieee_regulatory_certification_data_list.read_value()).hex()) + print( + color('System ID: ', 'green'), + await device_information_service.system_id.read_value(), + ) + if ( + device_information_service.ieee_regulatory_certification_data_list + is not None + ): + print( + color('Regulatory Certification:', 'green'), + ( + # pylint: disable-next=line-too-long + await device_information_service.ieee_regulatory_certification_data_list.read_value() + ).hex(), + ) # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/device_information_server.py b/examples/device_information_server.py index 9c3b6b1..d437cae 100644 --- a/examples/device_information_server.py +++ b/examples/device_information_server.py @@ -39,21 +39,26 @@ async def main(): # Add a Device Information Service to the GATT sever device_information_service = DeviceInformationService( - manufacturer_name = 'ACME', - model_number = 'AB-102', - serial_number = '7654321', - hardware_revision = '1.1.3', - software_revision = '2.5.6', - system_id = (0x123456, 0x8877665544) + manufacturer_name='ACME', + model_number='AB-102', + serial_number='7654321', + hardware_revision='1.1.3', + software_revision='2.5.6', + system_id=(0x123456, 0x8877665544), ) device.add_service(device_information_service) # Set the advertising data device.advertising_data = bytes( - AdvertisingData([ - (AdvertisingData.COMPLETE_LOCAL_NAME, bytes('Bumble Device', 'utf-8')), - (AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)) - ]) + AdvertisingData( + [ + ( + AdvertisingData.COMPLETE_LOCAL_NAME, + bytes('Bumble Device', 'utf-8'), + ), + (AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)), + ] + ) ) # Go! @@ -61,6 +66,7 @@ async def main(): await device.start_advertising(auto_restart=True) await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/heart_rate_client.py b/examples/heart_rate_client.py new file mode 100644 index 0000000..ecfcffb --- /dev/null +++ b/examples/heart_rate_client.py @@ -0,0 +1,74 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import asyncio +import sys +import os +import logging +from bumble.colors import color +from bumble.device import Device +from bumble.transport import open_transport +from bumble.profiles.heart_rate_service import HeartRateServiceProxy + + +# ----------------------------------------------------------------------------- +async def main(): + if len(sys.argv) != 3: + print('Usage: heart_rate_client.py <transport-spec> <bluetooth-address>') + print('example: heart_rate_client.py usb:0 E1:CA:72:48:C4:E8') + return + + print('<<< connecting to HCI...') + async with await open_transport(sys.argv[1]) as (hci_source, hci_sink): + print('<<< connected') + + # Create and start a device + device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink) + await device.power_on() + + # Connect to the peer + target_address = sys.argv[2] + print(f'=== Connecting to {target_address}...') + async with device.connect_as_gatt(target_address) as peer: + print(f'=== Connected to {peer}') + + heart_rate_service = peer.create_service_proxy(HeartRateServiceProxy) + + # Check that the service was found + if not heart_rate_service: + print('!!! Service not found') + return + + # Read the body sensor location + if heart_rate_service.body_sensor_location: + location = await heart_rate_service.body_sensor_location.read_value() + print(color('Sensor Location:', 'green'), location) + + # Subscribe to the heart rate measurement + if heart_rate_service.heart_rate_measurement: + await heart_rate_service.heart_rate_measurement.subscribe( + lambda value: print( + f'{color("Heart Rate Measurement:", "green")} {value}' + ) + ) + + await peer.sustain() + + +# ----------------------------------------------------------------------------- +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +asyncio.run(main()) diff --git a/examples/heart_rate_server.py b/examples/heart_rate_server.py new file mode 100644 index 0000000..32f53b1 --- /dev/null +++ b/examples/heart_rate_server.py @@ -0,0 +1,115 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import sys +import time +import math +import random +import struct +import logging +import asyncio +import os + +from bumble.core import AdvertisingData +from bumble.device import Device +from bumble.transport import open_transport_or_link +from bumble.profiles.device_information_service import DeviceInformationService +from bumble.profiles.heart_rate_service import HeartRateService + + +# ----------------------------------------------------------------------------- +async def main(): + if len(sys.argv) != 3: + print('Usage: python heart_rate_server.py <device-config> <transport-spec>') + print('example: python heart_rate_server.py device1.json usb:0') + return + + async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink): + device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink) + + # Keep track of accumulated expended energy + energy_start_time = time.time() + + def reset_energy_expended(): + nonlocal energy_start_time + energy_start_time = time.time() + + # Add a Device Information Service and Heart Rate Service to the GATT sever + device_information_service = DeviceInformationService( + manufacturer_name='ACME', + model_number='HR-102', + serial_number='7654321', + hardware_revision='1.1.3', + software_revision='2.5.6', + system_id=(0x123456, 0x8877665544), + ) + + heart_rate_service = HeartRateService( + read_heart_rate_measurement=lambda _: HeartRateService.HeartRateMeasurement( + heart_rate=100 + int(50 * math.sin(time.time() * math.pi / 60)), + sensor_contact_detected=random.choice((True, False, None)), + energy_expended=random.choice( + (int((time.time() - energy_start_time) * 100), None) + ), + rr_intervals=random.choice( + ( + ( + random.randint(900, 1100) / 1000, + random.randint(900, 1100) / 1000, + ), + None, + ) + ), + ), + body_sensor_location=HeartRateService.BodySensorLocation.WRIST, + reset_energy_expended=lambda _: reset_energy_expended(), + ) + + device.add_services([device_information_service, heart_rate_service]) + + # Set the advertising data + device.advertising_data = bytes( + AdvertisingData( + [ + ( + AdvertisingData.COMPLETE_LOCAL_NAME, + bytes('Bumble Heart', 'utf-8'), + ), + ( + AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, + bytes(heart_rate_service.uuid), + ), + (AdvertisingData.APPEARANCE, struct.pack('<H', 0x0340)), + ] + ) + ) + + # Go! + await device.power_on() + await device.start_advertising(auto_restart=True) + + # Notify every 3 seconds + while True: + await asyncio.sleep(3.0) + await device.notify_subscribers( + heart_rate_service.heart_rate_measurement_characteristic + ) + + +# ----------------------------------------------------------------------------- +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +asyncio.run(main()) diff --git a/examples/hfp_handsfree.html b/examples/hfp_handsfree.html index ab1ce3d..a86fc4a 100644 --- a/examples/hfp_handsfree.html +++ b/examples/hfp_handsfree.html @@ -76,4 +76,4 @@ input, label { } </script> </body> -</html>
\ No newline at end of file +</html> diff --git a/examples/hfp_handsfree.json b/examples/hfp_handsfree.json index 5d46a80..50d7841 100644 --- a/examples/hfp_handsfree.json +++ b/examples/hfp_handsfree.json @@ -1,4 +1,6 @@ { "name": "Bumble Hands-Free", - "class_of_device": 2360324 + "class_of_device": 2360324, + "keystore": "JsonKeyStore", + "le_enabled": false } diff --git a/examples/keyboard.html b/examples/keyboard.html index 979de5e..6ad83a7 100644 --- a/examples/keyboard.html +++ b/examples/keyboard.html @@ -58,4 +58,4 @@ } </script> </body> -</html>
\ No newline at end of file +</html> diff --git a/examples/keyboard.py b/examples/keyboard.py index ddb9f0f..314a805 100644 --- a/examples/keyboard.py +++ b/examples/keyboard.py @@ -20,9 +20,9 @@ import sys import os import logging import struct -import websockets import json -from colors import color +import websockets +from bumble.colors import color from bumble.core import AdvertisingData from bumble.device import Device, Connection, Peer @@ -34,8 +34,8 @@ from bumble.gatt import ( Characteristic, CharacteristicValue, GATT_DEVICE_INFORMATION_SERVICE, - GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE, - GATT_DEVICE_BATTERY_SERVICE, + GATT_HUMAN_INTERFACE_DEVICE_SERVICE, + GATT_BATTERY_SERVICE, GATT_BATTERY_LEVEL_CHARACTERISTIC, GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, GATT_REPORT_CHARACTERISTIC, @@ -43,59 +43,95 @@ from bumble.gatt import ( GATT_PROTOCOL_MODE_CHARACTERISTIC, GATT_HID_INFORMATION_CHARACTERISTIC, GATT_HID_CONTROL_POINT_CHARACTERISTIC, - GATT_REPORT_REFERENCE_DESCRIPTOR + GATT_REPORT_REFERENCE_DESCRIPTOR, ) # ----------------------------------------------------------------------------- # Protocol Modes -HID_BOOT_PROTOCOL = 0x00 +HID_BOOT_PROTOCOL = 0x00 HID_REPORT_PROTOCOL = 0x01 # Report Types -HID_INPUT_REPORT = 0x01 -HID_OUTPUT_REPORT = 0x02 +HID_INPUT_REPORT = 0x01 +HID_OUTPUT_REPORT = 0x02 HID_FEATURE_REPORT = 0x03 # Report Map -HID_KEYBOARD_REPORT_MAP = bytes([ - 0x05, 0x01, # Usage Page (Generic Desktop Ctrls) - 0x09, 0x06, # Usage (Keyboard) - 0xA1, 0x01, # Collection (Application) - 0x85, 0x01, # . Report ID (1) - 0x05, 0x07, # . Usage Page (Kbrd/Keypad) - 0x19, 0xE0, # . Usage Minimum (0xE0) - 0x29, 0xE7, # . Usage Maximum (0xE7) - 0x15, 0x00, # . Logical Minimum (0) - 0x25, 0x01, # . Logical Maximum (1) - 0x75, 0x01, # . Report Size (1) - 0x95, 0x08, # . Report Count (8) - 0x81, 0x02, # . Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x95, 0x01, # . Report Count (1) - 0x75, 0x08, # . Report Size (8) - 0x81, 0x01, # . Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x95, 0x06, # . Report Count (6) - 0x75, 0x08, # . Report Size (8) - 0x15, 0x00, # . Logical Minimum (0x00) - 0x25, 0x94, # . Logical Maximum (0x94) - 0x05, 0x07, # . Usage Page (Kbrd/Keypad) - 0x19, 0x00, # . Usage Minimum (0x00) - 0x29, 0x94, # . Usage Maximum (0x94) - 0x81, 0x00, # . Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 0x95, 0x05, # . Report Count (5) - 0x75, 0x01, # . Report Size (1) - 0x05, 0x08, # . Usage Page (LEDs) - 0x19, 0x01, # . Usage Minimum (Num Lock) - 0x29, 0x05, # . Usage Maximum (Kana) - 0x91, 0x02, # . Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0x95, 0x01, # . Report Count (1) - 0x75, 0x03, # . Report Size (3) - 0x91, 0x01, # . Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) - 0xC0 # End Collection -]) +HID_KEYBOARD_REPORT_MAP = bytes( + # pylint: disable=line-too-long + [ + 0x05, + 0x01, # Usage Page (Generic Desktop Controls) + 0x09, + 0x06, # Usage (Keyboard) + 0xA1, + 0x01, # Collection (Application) + 0x85, + 0x01, # . Report ID (1) + 0x05, + 0x07, # . Usage Page (Keyboard/Keypad) + 0x19, + 0xE0, # . Usage Minimum (0xE0) + 0x29, + 0xE7, # . Usage Maximum (0xE7) + 0x15, + 0x00, # . Logical Minimum (0) + 0x25, + 0x01, # . Logical Maximum (1) + 0x75, + 0x01, # . Report Size (1) + 0x95, + 0x08, # . Report Count (8) + 0x81, + 0x02, # . Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, + 0x01, # . Report Count (1) + 0x75, + 0x08, # . Report Size (8) + 0x81, + 0x01, # . Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, + 0x06, # . Report Count (6) + 0x75, + 0x08, # . Report Size (8) + 0x15, + 0x00, # . Logical Minimum (0x00) + 0x25, + 0x94, # . Logical Maximum (0x94) + 0x05, + 0x07, # . Usage Page (Keyboard/Keypad) + 0x19, + 0x00, # . Usage Minimum (0x00) + 0x29, + 0x94, # . Usage Maximum (0x94) + 0x81, + 0x00, # . Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) + 0x95, + 0x05, # . Report Count (5) + 0x75, + 0x01, # . Report Size (1) + 0x05, + 0x08, # . Usage Page (LEDs) + 0x19, + 0x01, # . Usage Minimum (Num Lock) + 0x29, + 0x05, # . Usage Maximum (Kana) + 0x91, + 0x02, # . Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0x95, + 0x01, # . Report Count (1) + 0x75, + 0x03, # . Report Size (3) + 0x91, + 0x01, # . Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) + 0xC0, # End Collection + ] +) # ----------------------------------------------------------------------------- +# pylint: disable=invalid-overridden-method class ServerListener(Device.Listener, Connection.Listener): def __init__(self, device): self.device = device @@ -111,7 +147,7 @@ class ServerListener(Device.Listener, Connection.Listener): # ----------------------------------------------------------------------------- -def on_hid_control_point_write(connection, value): +def on_hid_control_point_write(_connection, value): print(f'Control Point Write: {value}') @@ -126,52 +162,69 @@ async def keyboard_host(device, peer_address): connection = await device.connect(peer_address) await connection.pair() peer = Peer(connection) - await peer.discover_service(GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE) - hid_services = peer.get_services_by_uuid(GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE) + await peer.discover_service(GATT_HUMAN_INTERFACE_DEVICE_SERVICE) + hid_services = peer.get_services_by_uuid(GATT_HUMAN_INTERFACE_DEVICE_SERVICE) if not hid_services: print(color('!!! No HID service', 'red')) return await peer.discover_characteristics() - protocol_mode_characteristics = peer.get_characteristics_by_uuid(GATT_PROTOCOL_MODE_CHARACTERISTIC) + protocol_mode_characteristics = peer.get_characteristics_by_uuid( + GATT_PROTOCOL_MODE_CHARACTERISTIC + ) if not protocol_mode_characteristics: print(color('!!! No Protocol Mode characteristic', 'red')) return protocol_mode_characteristic = protocol_mode_characteristics[0] - hid_information_characteristics = peer.get_characteristics_by_uuid(GATT_HID_INFORMATION_CHARACTERISTIC) + hid_information_characteristics = peer.get_characteristics_by_uuid( + GATT_HID_INFORMATION_CHARACTERISTIC + ) if not hid_information_characteristics: print(color('!!! No HID Information characteristic', 'red')) return hid_information_characteristic = hid_information_characteristics[0] - report_map_characteristics = peer.get_characteristics_by_uuid(GATT_REPORT_MAP_CHARACTERISTIC) + report_map_characteristics = peer.get_characteristics_by_uuid( + GATT_REPORT_MAP_CHARACTERISTIC + ) if not report_map_characteristics: print(color('!!! No Report Map characteristic', 'red')) return report_map_characteristic = report_map_characteristics[0] - control_point_characteristics = peer.get_characteristics_by_uuid(GATT_HID_CONTROL_POINT_CHARACTERISTIC) + control_point_characteristics = peer.get_characteristics_by_uuid( + GATT_HID_CONTROL_POINT_CHARACTERISTIC + ) if not control_point_characteristics: print(color('!!! No Control Point characteristic', 'red')) return # control_point_characteristic = control_point_characteristics[0] - report_characteristics = peer.get_characteristics_by_uuid(GATT_REPORT_CHARACTERISTIC) + report_characteristics = peer.get_characteristics_by_uuid( + GATT_REPORT_CHARACTERISTIC + ) if not report_characteristics: print(color('!!! No Report characteristic', 'red')) return for i, characteristic in enumerate(report_characteristics): print(color('REPORT:', 'yellow'), characteristic) - if characteristic.properties & Characteristic.NOTIFY: + if characteristic.properties & Characteristic.Properties.NOTIFY: await peer.discover_descriptors(characteristic) - report_reference_descriptor = characteristic.get_descriptor(GATT_REPORT_REFERENCE_DESCRIPTOR) + report_reference_descriptor = characteristic.get_descriptor( + GATT_REPORT_REFERENCE_DESCRIPTOR + ) if report_reference_descriptor: report_reference = await peer.read_value(report_reference_descriptor) print(color(' Report Reference:', 'blue'), report_reference.hex()) else: report_reference = bytes([0, 0]) - await peer.subscribe(characteristic, lambda value, param=f'[{i}] {report_reference.hex()}': on_report(param, value)) + await peer.subscribe( + characteristic, + lambda value, param=f'[{i}] {report_reference.hex()}': on_report( + param, value + ), + ) protocol_mode = await peer.read_value(protocol_mode_characteristic) print(f'Protocol Mode: {protocol_mode.hex()}') @@ -188,81 +241,97 @@ async def keyboard_device(device, command): # Create an 'input report' characteristic to send keyboard reports to the host input_report_characteristic = Characteristic( GATT_REPORT_CHARACTERISTIC, - Characteristic.READ | Characteristic.WRITE | Characteristic.NOTIFY, + Characteristic.Properties.READ + | Characteristic.Properties.WRITE + | Characteristic.Properties.NOTIFY, Characteristic.READABLE | Characteristic.WRITEABLE, bytes([0, 0, 0, 0, 0, 0, 0, 0]), [ - Descriptor(GATT_REPORT_REFERENCE_DESCRIPTOR, Descriptor.READABLE, bytes([0x01, HID_INPUT_REPORT])) - ] + Descriptor( + GATT_REPORT_REFERENCE_DESCRIPTOR, + Descriptor.READABLE, + bytes([0x01, HID_INPUT_REPORT]), + ) + ], ) # Create an 'output report' characteristic to receive keyboard reports from the host output_report_characteristic = Characteristic( GATT_REPORT_CHARACTERISTIC, - Characteristic.READ | Characteristic.WRITE | Characteristic.WRITE_WITHOUT_RESPONSE, + Characteristic.Properties.READ + | Characteristic.Properties.WRITE + | Characteristic.WRITE_WITHOUT_RESPONSE, Characteristic.READABLE | Characteristic.WRITEABLE, bytes([0]), [ - Descriptor(GATT_REPORT_REFERENCE_DESCRIPTOR, Descriptor.READABLE, bytes([0x01, HID_OUTPUT_REPORT])) - ] + Descriptor( + GATT_REPORT_REFERENCE_DESCRIPTOR, + Descriptor.READABLE, + bytes([0x01, HID_OUTPUT_REPORT]), + ) + ], ) # Add the services to the GATT sever - device.add_services([ - Service( - GATT_DEVICE_INFORMATION_SERVICE, - [ - Characteristic( - GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, - Characteristic.READ, - Characteristic.READABLE, - 'Bumble' - ) - ] - ), - Service( - GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE, - [ - Characteristic( - GATT_PROTOCOL_MODE_CHARACTERISTIC, - Characteristic.READ, - Characteristic.READABLE, - bytes([HID_REPORT_PROTOCOL]) - ), - Characteristic( - GATT_HID_INFORMATION_CHARACTERISTIC, - Characteristic.READ, - Characteristic.READABLE, - bytes([0x11, 0x01, 0x00, 0x03]) # bcdHID=1.1, bCountryCode=0x00, Flags=RemoteWake|NormallyConnectable - ), - Characteristic( - GATT_HID_CONTROL_POINT_CHARACTERISTIC, - Characteristic.WRITE_WITHOUT_RESPONSE, - Characteristic.WRITEABLE, - CharacteristicValue(write=on_hid_control_point_write) - ), - Characteristic( - GATT_REPORT_MAP_CHARACTERISTIC, - Characteristic.READ, - Characteristic.READABLE, - HID_KEYBOARD_REPORT_MAP - ), - input_report_characteristic, - output_report_characteristic - ] - ), - Service( - GATT_DEVICE_BATTERY_SERVICE, - [ - Characteristic( - GATT_BATTERY_LEVEL_CHARACTERISTIC, - Characteristic.READ, - Characteristic.READABLE, - bytes([100]) - ) - ] - ) - ]) + device.add_services( + [ + Service( + GATT_DEVICE_INFORMATION_SERVICE, + [ + Characteristic( + GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + 'Bumble', + ) + ], + ), + Service( + GATT_HUMAN_INTERFACE_DEVICE_SERVICE, + [ + Characteristic( + GATT_PROTOCOL_MODE_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + bytes([HID_REPORT_PROTOCOL]), + ), + Characteristic( + GATT_HID_INFORMATION_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + # bcdHID=1.1, bCountryCode=0x00, + # Flags=RemoteWake|NormallyConnectable + bytes([0x11, 0x01, 0x00, 0x03]), + ), + Characteristic( + GATT_HID_CONTROL_POINT_CHARACTERISTIC, + Characteristic.WRITE_WITHOUT_RESPONSE, + Characteristic.WRITEABLE, + CharacteristicValue(write=on_hid_control_point_write), + ), + Characteristic( + GATT_REPORT_MAP_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + HID_KEYBOARD_REPORT_MAP, + ), + input_report_characteristic, + output_report_characteristic, + ], + ), + Service( + GATT_BATTERY_SERVICE, + [ + Characteristic( + GATT_BATTERY_LEVEL_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + bytes([100]), + ) + ], + ), + ] + ) # Debug print for attribute in device.gatt_server.attributes: @@ -270,13 +339,20 @@ async def keyboard_device(device, command): # Set the advertising data device.advertising_data = bytes( - AdvertisingData([ - (AdvertisingData.COMPLETE_LOCAL_NAME, bytes('Bumble Keyboard', 'utf-8')), - (AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, - bytes(GATT_DEVICE_HUMAN_INTERFACE_DEVICE_SERVICE)), - (AdvertisingData.APPEARANCE, struct.pack('<H', 0x03C1)), - (AdvertisingData.FLAGS, bytes([0x05])) - ]) + AdvertisingData( + [ + ( + AdvertisingData.COMPLETE_LOCAL_NAME, + bytes('Bumble Keyboard', 'utf-8'), + ), + ( + AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, + bytes(GATT_HUMAN_INTERFACE_DEVICE_SERVICE), + ), + (AdvertisingData.APPEARANCE, struct.pack('<H', 0x03C1)), + (AdvertisingData.FLAGS, bytes([0x05])), + ] + ) ) # Attach a listener @@ -288,7 +364,7 @@ async def keyboard_device(device, command): if command == 'web': # Start a Websocket server to receive events from a web page - async def serve(websocket, path): + async def serve(websocket, _path): while True: try: message = await websocket.recv() @@ -301,16 +377,24 @@ async def keyboard_device(device, command): key = parsed['key'] if len(key) == 1: code = ord(key) - if code >= ord('a') and code <= ord('z'): + if ord('a') <= code <= ord('z'): hid_code = 0x04 + code - ord('a') - input_report_characteristic.value = bytes([0, 0, hid_code, 0, 0, 0, 0, 0]) - await device.notify_subscribers(input_report_characteristic) + input_report_characteristic.value = bytes( + [0, 0, hid_code, 0, 0, 0, 0, 0] + ) + await device.notify_subscribers( + input_report_characteristic + ) elif message_type == 'keyup': - input_report_characteristic.value = bytes.fromhex('0000000000000000') + input_report_characteristic.value = bytes.fromhex( + '0000000000000000' + ) await device.notify_subscribers(input_report_characteristic) except websockets.exceptions.ConnectionClosedOK: pass + + # pylint: disable-next=no-member await websockets.serve(serve, 'localhost', 8989) await asyncio.get_event_loop().create_future() else: @@ -321,7 +405,9 @@ async def keyboard_device(device, command): # Keypress for the letter keycode = 0x04 + letter - 0x61 - input_report_characteristic.value = bytes([0, 0, keycode, 0, 0, 0, 0, 0]) + input_report_characteristic.value = bytes( + [0, 0, keycode, 0, 0, 0, 0, 0] + ) await device.notify_subscribers(input_report_characteristic) # Key release @@ -332,13 +418,20 @@ async def keyboard_device(device, command): # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 4: - print('Usage: python keyboard.py <device-config> <transport-spec> <command>') - print(' where <command> is one of:') - print(' connect <address> (run a keyboard host, connecting to a keyboard)') - print(' web (run a keyboard with keypress input from a web page, see keyboard.html') - print(' sim (run a keyboard simulation, emitting a canned sequence of keystrokes') + print( + 'Usage: python keyboard.py <device-config> <transport-spec> <command>' + ' where <command> is one of:\n' + ' connect <address> (run a keyboard host, connecting to a keyboard)\n' + ' web (run a keyboard with keypress input from a web page, ' + 'see keyboard.html\n' + ) + print( + ' sim (run a keyboard simulation, emitting a canned sequence of keystrokes' + ) print('example: python keyboard.py keyboard.json usb:0 sim') - print('example: python keyboard.py keyboard.json usb:0 connect A0:A1:A2:A3:A4:A5') + print( + 'example: python keyboard.py keyboard.json usb:0 connect A0:A1:A2:A3:A4:A5' + ) return async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink): @@ -349,11 +442,11 @@ async def main(): if command == 'connect': # Run as a Keyboard host await keyboard_host(device, sys.argv[4]) - elif command in {'sim', 'web'}: + elif command in ('sim', 'web'): # Run as a keyboard device await keyboard_device(device, command) # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_a2dp_info.py b/examples/run_a2dp_info.py index 0d6f66b..2f21cfa 100644 --- a/examples/run_a2dp_info.py +++ b/examples/run_a2dp_info.py @@ -20,19 +20,16 @@ import sys import os import logging -from colors import color +from bumble.colors import color from bumble.device import Device from bumble.transport import open_transport_or_link from bumble.core import ( BT_BR_EDR_TRANSPORT, BT_AVDTP_PROTOCOL_ID, BT_AUDIO_SINK_SERVICE, - BT_L2CAP_PROTOCOL_ID -) -from bumble.avdtp import ( - Protocol as AVDTP_Protocol, - find_avdtp_service_with_connection + BT_L2CAP_PROTOCOL_ID, ) +from bumble.avdtp import Protocol as AVDTP_Protocol from bumble.a2dp import make_audio_source_service_sdp_records from bumble.sdp import ( Client as SDP_Client, @@ -40,7 +37,7 @@ from bumble.sdp import ( DataElement, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, - SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, ) @@ -48,11 +45,14 @@ from bumble.sdp import ( def sdp_records(): service_record_handle = 0x00010001 return { - service_record_handle: make_audio_source_service_sdp_records(service_record_handle) + service_record_handle: make_audio_source_service_sdp_records( + service_record_handle + ) } # ----------------------------------------------------------------------------- +# pylint: disable-next=too-many-nested-blocks async def find_a2dp_service(device, connection): # Connect to the SDP Server sdp_client = SDP_Client(device) @@ -64,8 +64,8 @@ async def find_a2dp_service(device, connection): [ SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, - SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID - ] + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + ], ) print(color('==================================', 'blue')) @@ -78,8 +78,7 @@ async def find_a2dp_service(device, connection): # Service classes service_class_id_list = ServiceAttribute.find_attribute_in_list( - attribute_list, - SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID + attribute_list, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID ) if service_class_id_list: if service_class_id_list.value: @@ -89,8 +88,7 @@ async def find_a2dp_service(device, connection): # Protocol info protocol_descriptor_list = ServiceAttribute.find_attribute_in_list( - attribute_list, - SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID + attribute_list, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID ) if protocol_descriptor_list: print(color(' Protocol:', 'green')) @@ -103,27 +101,38 @@ async def find_a2dp_service(device, connection): if len(protocol_descriptor.value) >= 2: avdtp_version_major = protocol_descriptor.value[1].value >> 8 avdtp_version_minor = protocol_descriptor.value[1].value & 0xFF - print(f'{color(" AVDTP Version:", "cyan")} {avdtp_version_major}.{avdtp_version_minor}') + print( + f'{color(" AVDTP Version:", "cyan")} ' + f'{avdtp_version_major}.{avdtp_version_minor}' + ) service_version = (avdtp_version_major, avdtp_version_minor) # Profile info bluetooth_profile_descriptor_list = ServiceAttribute.find_attribute_in_list( - attribute_list, - SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID + attribute_list, SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID ) if bluetooth_profile_descriptor_list: if bluetooth_profile_descriptor_list.value: - if bluetooth_profile_descriptor_list.value[0].type == DataElement.SEQUENCE: - bluetooth_profile_descriptors = bluetooth_profile_descriptor_list.value + if ( + bluetooth_profile_descriptor_list.value[0].type + == DataElement.SEQUENCE + ): + bluetooth_profile_descriptors = ( + bluetooth_profile_descriptor_list.value + ) else: - # Sometimes, instead of a list of lists, we just find a list. Fix that + # Sometimes, instead of a list of lists, we just find a list. + # Fix that. bluetooth_profile_descriptors = [bluetooth_profile_descriptor_list] print(color(' Profiles:', 'green')) for bluetooth_profile_descriptor in bluetooth_profile_descriptors: version_major = bluetooth_profile_descriptor.value[1].value >> 8 version_minor = bluetooth_profile_descriptor.value[1].value & 0xFF - print(f' {bluetooth_profile_descriptor.value[0].value} - version {version_major}.{version_minor}') + print( + f' {bluetooth_profile_descriptor.value[0].value}' + f' - version {version_major}.{version_minor}' + ) await sdp_client.disconnect() return service_version @@ -147,7 +156,8 @@ async def main(): # Start the controller await device.power_on() - # Setup the SDP to expose a SRC service, in case the remote device queries us back + # Setup the SDP to expose a SRC service, in case the remote device queries us + # back device.sdp_service_records = sdp_records() # Connect to a peer @@ -184,5 +194,5 @@ async def main(): # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_a2dp_sink.py b/examples/run_a2dp_sink.py index bc193a1..b20f0d6 100644 --- a/examples/run_a2dp_sink.py +++ b/examples/run_a2dp_sink.py @@ -20,7 +20,6 @@ import sys import os import logging -from colors import color from bumble.device import Device from bumble.transport import open_transport_or_link from bumble.core import BT_BR_EDR_TRANSPORT @@ -28,7 +27,7 @@ from bumble.avdtp import ( AVDTP_AUDIO_MEDIA_TYPE, Protocol, Listener, - MediaCodecCapabilities + MediaCodecCapabilities, ) from bumble.a2dp import ( make_audio_sink_service_sdp_records, @@ -39,19 +38,19 @@ from bumble.a2dp import ( SBC_LOUDNESS_ALLOCATION_METHOD, SBC_STEREO_CHANNEL_MODE, SBC_JOINT_STEREO_CHANNEL_MODE, - SbcMediaCodecInformation + SbcMediaCodecInformation, ) -Context = { - 'output': None -} +Context = {'output': None} # ----------------------------------------------------------------------------- def sdp_records(): service_record_handle = 0x00010001 return { - service_record_handle: make_audio_sink_service_sdp_records(service_record_handle) + service_record_handle: make_audio_sink_service_sdp_records( + service_record_handle + ) } @@ -59,22 +58,25 @@ def sdp_records(): def codec_capabilities(): # NOTE: this shouldn't be hardcoded, but passed on the command line instead return MediaCodecCapabilities( - media_type = AVDTP_AUDIO_MEDIA_TYPE, - media_codec_type = A2DP_SBC_CODEC_TYPE, - media_codec_information = SbcMediaCodecInformation.from_lists( - sampling_frequencies = [48000, 44100, 32000, 16000], - channel_modes = [ + media_type=AVDTP_AUDIO_MEDIA_TYPE, + media_codec_type=A2DP_SBC_CODEC_TYPE, + media_codec_information=SbcMediaCodecInformation.from_lists( + sampling_frequencies=[48000, 44100, 32000, 16000], + channel_modes=[ SBC_MONO_CHANNEL_MODE, SBC_DUAL_CHANNEL_MODE, SBC_STEREO_CHANNEL_MODE, - SBC_JOINT_STEREO_CHANNEL_MODE + SBC_JOINT_STEREO_CHANNEL_MODE, ], - block_lengths = [4, 8, 12, 16], - subbands = [4, 8], - allocation_methods = [SBC_LOUDNESS_ALLOCATION_METHOD, SBC_SNR_ALLOCATION_METHOD], - minimum_bitpool_value = 2, - maximum_bitpool_value = 53 - ) + block_lengths=[4, 8, 12, 16], + subbands=[4, 8], + allocation_methods=[ + SBC_LOUDNESS_ALLOCATION_METHOD, + SBC_SNR_ALLOCATION_METHOD, + ], + minimum_bitpool_value=2, + maximum_bitpool_value=53, + ), ) @@ -87,10 +89,10 @@ def on_avdtp_connection(server): # ----------------------------------------------------------------------------- def on_rtp_packet(packet): - header = packet.payload[0] - fragmented = header >> 7 - start = (header >> 6) & 0x01 - last = (header >> 5) & 0x01 + header = packet.payload[0] + fragmented = header >> 7 + # start = (header >> 6) & 0x01 + # last = (header >> 5) & 0x01 number_of_frames = header & 0x0F if fragmented: @@ -104,7 +106,10 @@ def on_rtp_packet(packet): # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 4: - print('Usage: run_a2dp_sink.py <device-config> <transport-spec> <sbc-file> [<bt-addr>]') + print( + 'Usage: run_a2dp_sink.py <device-config> <transport-spec> <sbc-file> ' + '[<bt-addr>]' + ) print('example: run_a2dp_sink.py classic1.json usb:0 output.sbc') return @@ -133,7 +138,9 @@ async def main(): # Connect to the source target_address = sys.argv[4] print(f'=== Connecting to {target_address}...') - connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT) + connection = await device.connect( + target_address, transport=BT_BR_EDR_TRANSPORT + ) print(f'=== Connected to {connection.peer_address}!') # Request authentication @@ -159,5 +166,5 @@ async def main(): # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_a2dp_source.py b/examples/run_a2dp_source.py index 45abad9..2443518 100644 --- a/examples/run_a2dp_source.py +++ b/examples/run_a2dp_source.py @@ -20,7 +20,7 @@ import sys import os import logging -from colors import color +from bumble.colors import color from bumble.device import Device from bumble.transport import open_transport_or_link from bumble.core import BT_BR_EDR_TRANSPORT @@ -30,7 +30,7 @@ from bumble.avdtp import ( MediaCodecCapabilities, MediaPacketPump, Protocol, - Listener + Listener, ) from bumble.a2dp import ( SBC_JOINT_STEREO_CHANNEL_MODE, @@ -38,7 +38,7 @@ from bumble.a2dp import ( make_audio_source_service_sdp_records, A2DP_SBC_CODEC_TYPE, SbcMediaCodecInformation, - SbcPacketSource + SbcPacketSource, ) @@ -46,31 +46,36 @@ from bumble.a2dp import ( def sdp_records(): service_record_handle = 0x00010001 return { - service_record_handle: make_audio_source_service_sdp_records(service_record_handle) + service_record_handle: make_audio_source_service_sdp_records( + service_record_handle + ) } # ----------------------------------------------------------------------------- def codec_capabilities(): - # NOTE: this shouldn't be hardcoded, but should be inferred from the input file instead + # NOTE: this shouldn't be hardcoded, but should be inferred from the input file + # instead return MediaCodecCapabilities( - media_type = AVDTP_AUDIO_MEDIA_TYPE, - media_codec_type = A2DP_SBC_CODEC_TYPE, - media_codec_information = SbcMediaCodecInformation.from_discrete_values( - sampling_frequency = 44100, - channel_mode = SBC_JOINT_STEREO_CHANNEL_MODE, - block_length = 16, - subbands = 8, - allocation_method = SBC_LOUDNESS_ALLOCATION_METHOD, - minimum_bitpool_value = 2, - maximum_bitpool_value = 53 - ) + media_type=AVDTP_AUDIO_MEDIA_TYPE, + media_codec_type=A2DP_SBC_CODEC_TYPE, + media_codec_information=SbcMediaCodecInformation.from_discrete_values( + sampling_frequency=44100, + channel_mode=SBC_JOINT_STEREO_CHANNEL_MODE, + block_length=16, + subbands=8, + allocation_method=SBC_LOUDNESS_ALLOCATION_METHOD, + minimum_bitpool_value=2, + maximum_bitpool_value=53, + ), ) # ----------------------------------------------------------------------------- def on_avdtp_connection(read_function, protocol): - packet_source = SbcPacketSource(read_function, protocol.l2cap_channel.mtu, codec_capabilities()) + packet_source = SbcPacketSource( + read_function, protocol.l2cap_channel.mtu, codec_capabilities() + ) packet_pump = MediaPacketPump(packet_source.packets) protocol.add_source(packet_source.codec_capabilities, packet_pump) @@ -83,14 +88,18 @@ async def stream_packets(read_function, protocol): print('@@@', endpoint) # Select a sink - sink = protocol.find_remote_sink_by_codec(AVDTP_AUDIO_MEDIA_TYPE, A2DP_SBC_CODEC_TYPE) + sink = protocol.find_remote_sink_by_codec( + AVDTP_AUDIO_MEDIA_TYPE, A2DP_SBC_CODEC_TYPE + ) if sink is None: print(color('!!! no SBC sink found', 'red')) return print(f'### Selected sink: {sink.seid}') # Stream the packets - packet_source = SbcPacketSource(read_function, protocol.l2cap_channel.mtu, codec_capabilities()) + packet_source = SbcPacketSource( + read_function, protocol.l2cap_channel.mtu, codec_capabilities() + ) packet_pump = MediaPacketPump(packet_source.packets) source = protocol.add_source(packet_source.codec_capabilities, packet_pump) stream = await protocol.create_stream(source, sink) @@ -107,8 +116,13 @@ async def stream_packets(read_function, protocol): # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 4: - print('Usage: run_a2dp_source.py <device-config> <transport-spec> <sbc-file> [<bluetooth-address>]') - print('example: run_a2dp_source.py classic1.json usb:0 test.sbc E1:CA:72:48:C4:E8') + print( + 'Usage: run_a2dp_source.py <device-config> <transport-spec> <sbc-file> ' + '[<bluetooth-address>]' + ) + print( + 'example: run_a2dp_source.py classic1.json usb:0 test.sbc E1:CA:72:48:C4:E8' + ) return print('<<< connecting to HCI...') @@ -126,7 +140,8 @@ async def main(): await device.power_on() with open(sys.argv[3], 'rb') as sbc_file: - # NOTE: this should be using asyncio file reading, but blocking reads are good enough for testing + # NOTE: this should be using asyncio file reading, but blocking reads are + # good enough for testing async def read(byte_count): return sbc_file.read(byte_count) @@ -134,7 +149,9 @@ async def main(): # Connect to a peer target_address = sys.argv[4] print(f'=== Connecting to {target_address}...') - connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT) + connection = await device.connect( + target_address, transport=BT_BR_EDR_TRANSPORT + ) print(f'=== Connected to {connection.peer_address}!') # Request authentication @@ -148,7 +165,9 @@ async def main(): print('*** Encryption on') # Look for an A2DP service - avdtp_version = await find_avdtp_service_with_connection(device, connection) + avdtp_version = await find_avdtp_service_with_connection( + device, connection + ) if not avdtp_version: print(color('!!! no A2DP service found')) return @@ -161,7 +180,9 @@ async def main(): else: # Create a listener to wait for AVDTP connections listener = Listener(Listener.create_registrar(device), version=(1, 2)) - listener.on('connection', lambda protocol: on_avdtp_connection(read, protocol)) + listener.on( + 'connection', lambda protocol: on_avdtp_connection(read, protocol) + ) # Become connectable and wait for a connection await device.set_discoverable(True) @@ -171,5 +192,5 @@ async def main(): # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_advertiser.py b/examples/run_advertiser.py index 5201356..56b1b8b 100644 --- a/examples/run_advertiser.py +++ b/examples/run_advertiser.py @@ -16,33 +16,47 @@ # Imports # ----------------------------------------------------------------------------- import asyncio +import logging import sys import os +from bumble.device import AdvertisingType, Device +from bumble.hci import Address -from bumble.hci import * -from bumble.controller import * -from bumble.device import * -from bumble.transport import * -from bumble.host import * from bumble.transport import open_transport_or_link # ----------------------------------------------------------------------------- async def main(): - if len(sys.argv) != 3: - print('Usage: run_advertiser.py <config-file> <transport-spec>') - print('example: run_advertiser.py device1.json link-relay:ws://localhost:8888/test') + if len(sys.argv) < 3: + print( + 'Usage: run_advertiser.py <config-file> <transport-spec> [type] [address]' + ) + print('example: run_advertiser.py device1.json usb:0') return + if len(sys.argv) >= 4: + advertising_type = AdvertisingType(int(sys.argv[3])) + else: + advertising_type = AdvertisingType.UNDIRECTED_CONNECTABLE_SCANNABLE + + if advertising_type.is_directed: + if len(sys.argv) < 5: + print('<address> required for directed advertising') + return + target = Address(sys.argv[4]) + else: + target = None + print('<<< connecting to HCI...') async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink): print('<<< connected') device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink) await device.power_on() - await device.start_advertising() + await device.start_advertising(advertising_type=advertising_type, target=target) await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_asha_sink.py b/examples/run_asha_sink.py new file mode 100644 index 0000000..3e4955d --- /dev/null +++ b/examples/run_asha_sink.py @@ -0,0 +1,202 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import asyncio +import struct +import sys +import os +import logging + +from bumble.core import AdvertisingData +from bumble.device import Device +from bumble.transport import open_transport_or_link +from bumble.core import UUID +from bumble.gatt import Service, Characteristic, CharacteristicValue + + +# ----------------------------------------------------------------------------- +# Constants +# ----------------------------------------------------------------------------- +ASHA_SERVICE = UUID.from_16_bits(0xFDF0, 'Audio Streaming for Hearing Aid') +ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC = UUID( + '6333651e-c481-4a3e-9169-7c902aad37bb', 'ReadOnlyProperties' +) +ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC = UUID( + 'f0d4de7e-4a88-476c-9d9f-1937b0996cc0', 'AudioControlPoint' +) +ASHA_AUDIO_STATUS_CHARACTERISTIC = UUID( + '38663f1a-e711-4cac-b641-326b56404837', 'AudioStatus' +) +ASHA_VOLUME_CHARACTERISTIC = UUID('00e4ca9e-ab14-41e4-8823-f9e70c7e91df', 'Volume') +ASHA_LE_PSM_OUT_CHARACTERISTIC = UUID( + '2d410339-82b6-42aa-b34e-e2e01df8cc1a', 'LE_PSM_OUT' +) + + +# ----------------------------------------------------------------------------- +async def main(): + if len(sys.argv) != 4: + print( + 'Usage: python run_asha_sink.py <device-config> <transport-spec> ' + '<audio-file>' + ) + print('example: python run_asha_sink.py device1.json usb:0 audio_out.g722') + return + + audio_out = open(sys.argv[3], 'wb') + + async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink): + device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink) + + # Handler for audio control commands + def on_audio_control_point_write(_connection, value): + print('--- AUDIO CONTROL POINT Write:', value.hex()) + opcode = value[0] + if opcode == 1: + # Start + audio_type = ('Unknown', 'Ringtone', 'Phone Call', 'Media')[value[2]] + print( + f'### START: codec={value[1]}, audio_type={audio_type}, ' + f'volume={value[3]}, otherstate={value[4]}' + ) + elif opcode == 2: + print('### STOP') + elif opcode == 3: + print(f'### STATUS: connected={value[1]}') + + # Respond with a status + asyncio.create_task( + device.notify_subscribers(audio_status_characteristic, force=True) + ) + + # Handler for volume control + def on_volume_write(_connection, value): + print('--- VOLUME Write:', value[0]) + + # Register an L2CAP CoC server + def on_coc(channel): + def on_data(data): + print('<<< Voice data received:', data.hex()) + audio_out.write(data) + + channel.sink = on_data + + psm = device.register_l2cap_channel_server(0, on_coc, 8) + print(f'### LE_PSM_OUT = {psm}') + + # Add the ASHA service to the GATT server + read_only_properties_characteristic = Characteristic( + ASHA_READ_ONLY_PROPERTIES_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + bytes( + [ + 0x01, # Version + 0x00, # Device Capabilities [Left, Monaural] + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, # HiSyncId + 0x01, # Feature Map [LE CoC audio output streaming supported] + 0x00, + 0x00, # Render Delay + 0x00, + 0x00, # RFU + 0x02, + 0x00, # Codec IDs [G.722 at 16 kHz] + ] + ), + ) + audio_control_point_characteristic = Characteristic( + ASHA_AUDIO_CONTROL_POINT_CHARACTERISTIC, + Characteristic.Properties.WRITE | Characteristic.WRITE_WITHOUT_RESPONSE, + Characteristic.WRITEABLE, + CharacteristicValue(write=on_audio_control_point_write), + ) + audio_status_characteristic = Characteristic( + ASHA_AUDIO_STATUS_CHARACTERISTIC, + Characteristic.Properties.READ | Characteristic.Properties.NOTIFY, + Characteristic.READABLE, + bytes([0]), + ) + volume_characteristic = Characteristic( + ASHA_VOLUME_CHARACTERISTIC, + Characteristic.WRITE_WITHOUT_RESPONSE, + Characteristic.WRITEABLE, + CharacteristicValue(write=on_volume_write), + ) + le_psm_out_characteristic = Characteristic( + ASHA_LE_PSM_OUT_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + struct.pack('<H', psm), + ) + device.add_service( + Service( + ASHA_SERVICE, + [ + read_only_properties_characteristic, + audio_control_point_characteristic, + audio_status_characteristic, + volume_characteristic, + le_psm_out_characteristic, + ], + ) + ) + + # Set the advertising data + device.advertising_data = bytes( + AdvertisingData( + [ + (AdvertisingData.COMPLETE_LOCAL_NAME, bytes(device.name, 'utf-8')), + (AdvertisingData.FLAGS, bytes([0x06])), + ( + AdvertisingData.INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, + bytes(ASHA_SERVICE), + ), + ( + AdvertisingData.SERVICE_DATA_16_BIT_UUID, + bytes(ASHA_SERVICE) + + bytes( + [ + 0x01, # Protocol Version + 0x00, # Capability + 0x01, + 0x02, + 0x03, + 0x04, # Truncated HiSyncID + ] + ), + ), + ] + ) + ) + + # Go! + await device.power_on() + await device.start_advertising(auto_restart=True) + + await hci_source.wait_for_termination() + + +# ----------------------------------------------------------------------------- +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +asyncio.run(main()) diff --git a/examples/run_classic_connect.py b/examples/run_classic_connect.py index d6842fe..bb46bf7 100644 --- a/examples/run_classic_connect.py +++ b/examples/run_classic_connect.py @@ -19,19 +19,26 @@ import asyncio import sys import os import logging -from colors import color +from bumble.colors import color from bumble.device import Device from bumble.transport import open_transport_or_link from bumble.core import BT_BR_EDR_TRANSPORT, BT_L2CAP_PROTOCOL_ID -from bumble.sdp import Client as SDP_Client, SDP_PUBLIC_BROWSE_ROOT, SDP_ALL_ATTRIBUTES_RANGE +from bumble.sdp import ( + Client as SDP_Client, + SDP_PUBLIC_BROWSE_ROOT, + SDP_ALL_ATTRIBUTES_RANGE, +) # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 3: - print('Usage: run_classic_connect.py <device-config> <transport-spec> <bluetooth-address>') - print('example: run_classic_connect.py classic1.json usb:04b4:f901 E1:CA:72:48:C4:E8') + print( + 'Usage: run_classic_connect.py <device-config> <transport-spec> ' + '<bluetooth-addresses..>' + ) + print('example: run_classic_connect.py classic1.json usb:0 E1:CA:72:48:C4:E8') return print('<<< connecting to HCI...') @@ -43,8 +50,7 @@ async def main(): device.classic_enabled = True await device.power_on() - # Connect to a peer - target_address = sys.argv[3] + async def connect(target_address): print(f'=== Connecting to {target_address}...') connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT) print(f'=== Connected to {connection.peer_address}!') @@ -54,28 +60,52 @@ async def main(): await sdp_client.connect(connection) # List all services in the root browse group - service_record_handles = await sdp_client.search_services([SDP_PUBLIC_BROWSE_ROOT]) + service_record_handles = await sdp_client.search_services( + [SDP_PUBLIC_BROWSE_ROOT] + ) print(color('\n==================================', 'blue')) print(color('SERVICES:', 'yellow'), service_record_handles) # For each service in the root browse group, get all its attributes for service_record_handle in service_record_handles: - attributes = await sdp_client.get_attributes(service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE]) + attributes = await sdp_client.get_attributes( + service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE] + ) print(color(f'SERVICE {service_record_handle:04X} attributes:', 'yellow')) for attribute in attributes: - print(' ', attribute.to_string(color=True)) + print(' ', attribute.to_string(with_colors=True)) # Search for services with an L2CAP service attribute - search_result = await sdp_client.search_attributes([BT_L2CAP_PROTOCOL_ID], [SDP_ALL_ATTRIBUTES_RANGE]) + search_result = await sdp_client.search_attributes( + [BT_L2CAP_PROTOCOL_ID], [SDP_ALL_ATTRIBUTES_RANGE] + ) print(color('\n==================================', 'blue')) print(color('SEARCH RESULTS:', 'yellow')) for attribute_list in search_result: print(color('SERVICE:', 'green')) - print(' ' + '\n '.join([attribute.to_string(color=True) for attribute in attribute_list])) + print( + ' ' + + '\n '.join( + [ + attribute.to_string(with_colors=True) + for attribute in attribute_list + ] + ) + ) await sdp_client.disconnect() await hci_source.wait_for_termination() + # Connect to a peer + target_addresses = sys.argv[3:] + await asyncio.wait( + [ + asyncio.create_task(connect(target_address)) + for target_address in target_addresses + ] + ) + + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_classic_discoverable.py b/examples/run_classic_discoverable.py index 5cdbc27..076a9ec 100644 --- a/examples/run_classic_discoverable.py +++ b/examples/run_classic_discoverable.py @@ -30,48 +30,62 @@ from bumble.sdp import ( SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, - SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, ) from bumble.core import ( BT_AUDIO_SINK_SERVICE, BT_L2CAP_PROTOCOL_ID, BT_AVDTP_PROTOCOL_ID, - BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE + BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE, ) # ----------------------------------------------------------------------------- SDP_SERVICE_RECORDS = { 0x00010001: [ - ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(0x00010001)), - ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT) - ])), + ServiceAttribute( + SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, + DataElement.unsigned_integer_32(0x00010001), + ), + ServiceAttribute( + SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, + DataElement.sequence([DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)]), + ), ServiceAttribute( SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, - DataElement.sequence([DataElement.uuid(BT_AUDIO_SINK_SERVICE)]) + DataElement.sequence([DataElement.uuid(BT_AUDIO_SINK_SERVICE)]), ), ServiceAttribute( SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, - DataElement.sequence([ - DataElement.sequence([ - DataElement.uuid(BT_L2CAP_PROTOCOL_ID), - DataElement.unsigned_integer_16(25) - ]), - DataElement.sequence([ - DataElement.uuid(BT_AVDTP_PROTOCOL_ID), - DataElement.unsigned_integer_16(256) - ]) - ]) + DataElement.sequence( + [ + DataElement.sequence( + [ + DataElement.uuid(BT_L2CAP_PROTOCOL_ID), + DataElement.unsigned_integer_16(25), + ] + ), + DataElement.sequence( + [ + DataElement.uuid(BT_AVDTP_PROTOCOL_ID), + DataElement.unsigned_integer_16(256), + ] + ), + ] + ), ), ServiceAttribute( SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, - DataElement.sequence([ - DataElement.sequence([ - DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE), - DataElement.unsigned_integer_16(256) - ]) - ]) - ) + DataElement.sequence( + [ + DataElement.sequence( + [ + DataElement.uuid(BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE), + DataElement.unsigned_integer_16(256), + ] + ) + ] + ), + ), ] } @@ -99,6 +113,7 @@ async def main(): await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_classic_discovery.py b/examples/run_classic_discovery.py index b0ab5ee..569c8b3 100644 --- a/examples/run_classic_discovery.py +++ b/examples/run_classic_discovery.py @@ -19,7 +19,7 @@ import asyncio import sys import os import logging -from colors import color +from bumble.colors import color from bumble.device import Device from bumble.transport import open_transport_or_link @@ -28,17 +28,28 @@ from bumble.core import DeviceClass # ----------------------------------------------------------------------------- class DiscoveryListener(Device.Listener): - def on_inquiry_result(self, address, class_of_device, eir_data, rssi): - service_classes, major_device_class, minor_device_class = DeviceClass.split_class_of_device(class_of_device) + def on_inquiry_result(self, address, class_of_device, data, rssi): + ( + service_classes, + major_device_class, + minor_device_class, + ) = DeviceClass.split_class_of_device(class_of_device) separator = '\n ' print(f'>>> {color(address, "yellow")}:') print(f' Device Class (raw): {class_of_device:06X}') - print(f' Device Major Class: {DeviceClass.major_device_class_name(major_device_class)}') - print(f' Device Minor Class: {DeviceClass.minor_device_class_name(major_device_class, minor_device_class)}') - print(f' Device Services: {", ".join(DeviceClass.service_class_labels(service_classes))}') + major_class_name = DeviceClass.major_device_class_name(major_device_class) + print(' Device Major Class: ' f'{major_class_name}') + minor_class_name = DeviceClass.minor_device_class_name( + major_device_class, minor_device_class + ) + print(' Device Minor Class: ' f'{minor_class_name}') + print( + ' Device Services: ' + f'{", ".join(DeviceClass.service_class_labels(service_classes))}' + ) print(f' RSSI: {rssi}') - if eir_data.ad_structures: - print(f' {eir_data.to_string(separator)}') + if data.ad_structures: + print(f' {data.to_string(separator)}') # ----------------------------------------------------------------------------- @@ -59,6 +70,7 @@ async def main(): await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_connect_and_encrypt.py b/examples/run_connect_and_encrypt.py index 0ee868a..b541a0e 100644 --- a/examples/run_connect_and_encrypt.py +++ b/examples/run_connect_and_encrypt.py @@ -27,8 +27,13 @@ from bumble.transport import open_transport_or_link # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 3: - print('Usage: run_connect_and_encrypt.py <device-config> <transport-spec> <bluetooth-address>') - print('example: run_connect_and_encrypt.py device1.json usb:0 E1:CA:72:48:C4:E8') + print( + 'Usage: run_connect_and_encrypt.py <device-config> <transport-spec> ' + '<bluetooth-address>' + ) + print( + 'example: run_connect_and_encrypt.py device1.json usb:0 E1:CA:72:48:C4:E8' + ) return print('<<< connecting to HCI...') @@ -53,6 +58,7 @@ async def main(): await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_controller.py b/examples/run_controller.py index d8295d4..596ac8b 100644 --- a/examples/run_controller.py +++ b/examples/run_controller.py @@ -20,20 +20,32 @@ import asyncio import sys import os -from bumble.hci import * -from bumble.controller import * -from bumble.host import * -from bumble.device import * -from bumble.transport import * -from bumble.link import * +from bumble.gatt import ( + GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, + GATT_DEVICE_INFORMATION_SERVICE, + GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, + Characteristic, + Descriptor, + Service, +) +from bumble.device import Device +from bumble.host import Host +from bumble.controller import Controller +from bumble.link import LocalLink from bumble.transport import open_transport_or_link # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) != 4: - print('Usage: run_controller.py <controller-address> <device-config> <transport-spec>') - print('example: run_controller.py F2:F3:F4:F5:F6:F7 device1.json udp:0.0.0.0:22333,172.16.104.161:22333') + print( + 'Usage: run_controller.py <controller-address> <device-config> ' + '<transport-spec>' + ) + print( + 'example: run_controller.py F2:F3:F4:F5:F6:F7 device1.json ' + 'udp:0.0.0.0:22333,172.16.104.161:22333' + ) return print('>>> connecting to HCI...') @@ -44,11 +56,13 @@ async def main(): link = LocalLink() # Create a first controller using the packet source/sink as its host interface - controller1 = Controller('C1', host_source = hci_source, host_sink = hci_sink, link = link) + controller1 = Controller( + 'C1', host_source=hci_source, host_sink=hci_sink, link=link + ) controller1.random_address = sys.argv[1] # Create a second controller using the same link - controller2 = Controller('C2', link = link) + controller2 = Controller('C2', link=link) # Create a host for the second controller host = Host() @@ -59,17 +73,21 @@ async def main(): device.host = host # Add some basic services to the device's GATT server - descriptor = Descriptor(GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, Descriptor.READABLE, 'My Description') + descriptor = Descriptor( + GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, + Descriptor.READABLE, + 'My Description', + ) manufacturer_name_characteristic = Characteristic( GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, - Characteristic.READ, + Characteristic.Properties.READ, Characteristic.READABLE, "Fitbit", - [descriptor] + [descriptor], + ) + device_info_service = Service( + GATT_DEVICE_INFORMATION_SERVICE, [manufacturer_name_characteristic] ) - device_info_service = Service(GATT_DEVICE_INFORMATION_SERVICE, [ - manufacturer_name_characteristic - ]) device.add_service(device_info_service) # Debug print @@ -82,6 +100,7 @@ async def main(): await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_controller_with_scanner.py b/examples/run_controller_with_scanner.py index 88bc1f8..9603cff 100644 --- a/examples/run_controller_with_scanner.py +++ b/examples/run_controller_with_scanner.py @@ -19,7 +19,7 @@ import logging import asyncio import sys import os -from colors import color +from bumble.colors import color from bumble.device import Device from bumble.controller import Controller @@ -29,15 +29,19 @@ from bumble.transport import open_transport_or_link # ----------------------------------------------------------------------------- class ScannerListener(Device.Listener): - def on_advertisement(self, address, ad_data, rssi, connectable): - address_type_string = ('P', 'R', 'PI', 'RI')[address.address_type] - address_color = 'yellow' if connectable else 'red' + def on_advertisement(self, advertisement): + address_type_string = ('P', 'R', 'PI', 'RI')[advertisement.address.address_type] + address_color = 'yellow' if advertisement.is_connectable else 'red' if address_type_string.startswith('P'): type_color = 'green' else: type_color = 'cyan' - print(f'>>> {color(address, address_color)} [{color(address_type_string, type_color)}]: RSSI={rssi}, {ad_data}') + print( + f'>>> {color(advertisement.address, address_color)} ' + f'[{color(address_type_string, type_color)}]: ' + f'RSSI={advertisement.rssi}, {advertisement.data}' + ) # ----------------------------------------------------------------------------- @@ -55,20 +59,25 @@ async def main(): link = LocalLink() # Create a first controller using the packet source/sink as its host interface - controller1 = Controller('C1', host_source = hci_source, host_sink = hci_sink, link = link) + controller1 = Controller( + 'C1', host_source=hci_source, host_sink=hci_sink, link=link + ) controller1.address = 'E0:E1:E2:E3:E4:E5' # Create a second controller using the same link - controller2 = Controller('C2', link = link) + controller2 = Controller('C2', link=link) # Create a device with a scanner listener - device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', controller2, controller2) + device = Device.with_hci( + 'Bumble', 'F0:F1:F2:F3:F4:F5', controller2, controller2 + ) device.listener = ScannerListener() await device.power_on() await device.start_scanning() await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_device_with_snooper.py b/examples/run_device_with_snooper.py new file mode 100644 index 0000000..69a187f --- /dev/null +++ b/examples/run_device_with_snooper.py @@ -0,0 +1,51 @@ +# Copyright 2021-2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import asyncio +import sys +import os +import logging +from bumble.colors import color + +from bumble.device import Device +from bumble.transport import open_transport_or_link +from bumble.snoop import BtSnooper + +# ----------------------------------------------------------------------------- +async def main(): + if len(sys.argv) != 3: + print('Usage: run_device_with_snooper.py <transport-spec> <snoop-file>') + print('example: run_device_with_snooper.py usb:0 btsnoop.log') + return + + print('<<< connecting to HCI...') + async with await open_transport_or_link(sys.argv[1]) as (hci_source, hci_sink): + print('<<< connected') + + device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink) + + with open(sys.argv[2], "wb") as snoop_file: + device.host.snooper = BtSnooper(snoop_file) + await device.power_on() + await device.start_scanning() + + await hci_source.wait_for_termination() + + +# ----------------------------------------------------------------------------- +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +asyncio.run(main()) diff --git a/examples/run_gatt_client.py b/examples/run_gatt_client.py index 5af86fb..dcf8a1b 100644 --- a/examples/run_gatt_client.py +++ b/examples/run_gatt_client.py @@ -19,7 +19,7 @@ import asyncio import sys import os import logging -from colors import color +from bumble.colors import color from bumble.core import ProtocolError from bumble.device import Device, Peer @@ -34,6 +34,7 @@ class Listener(Device.Listener): self.device = device @AsyncRunner.run_in_task() + # pylint: disable=invalid-overridden-method async def on_connection(self, connection): print(f'=== Connected to {connection}') @@ -70,7 +71,10 @@ class Listener(Device.Listener): # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 3: - print('Usage: run_gatt_client.py <device-config> <transport-spec> [<bluetooth-address>]') + print( + 'Usage: run_gatt_client.py <device-config> <transport-spec> ' + '[<bluetooth-address>]' + ) print('example: run_gatt_client.py device1.json usb:0 E1:CA:72:48:C4:E8') return @@ -93,6 +97,7 @@ async def main(): await asyncio.get_running_loop().create_future() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_gatt_client_and_server.py b/examples/run_gatt_client_and_server.py index 940b1a8..609fe18 100644 --- a/examples/run_gatt_client_and_server.py +++ b/examples/run_gatt_client_and_server.py @@ -18,7 +18,7 @@ import asyncio import os import logging -from colors import color +from bumble.colors import color from bumble.core import ProtocolError from bumble.controller import Controller @@ -32,7 +32,7 @@ from bumble.gatt import ( show_services, GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, - GATT_DEVICE_INFORMATION_SERVICE + GATT_DEVICE_INFORMATION_SERVICE, ) @@ -48,32 +48,36 @@ async def main(): link = LocalLink() # Setup a stack for the client - client_controller = Controller("client controller", link = link) + client_controller = Controller("client controller", link=link) client_host = Host() client_host.controller = client_controller - client_device = Device("client", address = 'F0:F1:F2:F3:F4:F5', host = client_host) + client_device = Device("client", address='F0:F1:F2:F3:F4:F5', host=client_host) await client_device.power_on() # Setup a stack for the server - server_controller = Controller("server controller", link = link) + server_controller = Controller("server controller", link=link) server_host = Host() server_host.controller = server_controller - server_device = Device("server", address = 'F6:F7:F8:F9:FA:FB', host = server_host) + server_device = Device("server", address='F6:F7:F8:F9:FA:FB', host=server_host) server_device.listener = ServerListener() await server_device.power_on() # Add a few entries to the device's GATT server - descriptor = Descriptor(GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, Descriptor.READABLE, 'My Description') + descriptor = Descriptor( + GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, + Descriptor.READABLE, + 'My Description', + ) manufacturer_name_characteristic = Characteristic( GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, - Characteristic.READ, + Characteristic.Properties.READ, Characteristic.READABLE, "Fitbit", - [descriptor] + [descriptor], + ) + device_info_service = Service( + GATT_DEVICE_INFORMATION_SERVICE, [manufacturer_name_characteristic] ) - device_info_service = Service(GATT_DEVICE_INFORMATION_SERVICE, [ - manufacturer_name_characteristic - ]) server_device.add_service(device_info_service) # Connect the client to the server @@ -109,6 +113,7 @@ async def main(): await asyncio.get_running_loop().create_future() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_gatt_server.py b/examples/run_gatt_server.py index 099d589..46d42a2 100644 --- a/examples/run_gatt_server.py +++ b/examples/run_gatt_server.py @@ -22,10 +22,7 @@ import logging from bumble.device import Device, Connection from bumble.transport import open_transport_or_link -from bumble.att import ( - ATT_Error, - ATT_INSUFFICIENT_ENCRYPTION_ERROR -) +from bumble.att import ATT_Error, ATT_INSUFFICIENT_ENCRYPTION_ERROR from bumble.gatt import ( Service, Characteristic, @@ -33,7 +30,7 @@ from bumble.gatt import ( Descriptor, GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, - GATT_DEVICE_INFORMATION_SERVICE + GATT_DEVICE_INFORMATION_SERVICE, ) @@ -63,8 +60,8 @@ def my_custom_read_with_error(connection): print('----- READ from', connection, '[returning error]') if connection.is_encrypted: return bytes([123]) - else: - raise ATT_Error(ATT_INSUFFICIENT_ENCRYPTION_ERROR) + + raise ATT_Error(ATT_INSUFFICIENT_ENCRYPTION_ERROR) def my_custom_write_with_error(connection, value): @@ -76,7 +73,10 @@ def my_custom_write_with_error(connection, value): # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 3: - print('Usage: run_gatt_server.py <device-config> <transport-spec> [<bluetooth-address>]') + print( + 'Usage: run_gatt_server.py <device-config> <transport-spec> ' + '[<bluetooth-address>]' + ) print('example: run_gatt_server.py device1.json usb:0 E1:CA:72:48:C4:E8') return @@ -89,39 +89,45 @@ async def main(): device.listener = Listener(device) # Add a few entries to the device's GATT server - descriptor = Descriptor(GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, Descriptor.READABLE, 'My Description') + descriptor = Descriptor( + GATT_CHARACTERISTIC_USER_DESCRIPTION_DESCRIPTOR, + Descriptor.READABLE, + 'My Description', + ) manufacturer_name_characteristic = Characteristic( GATT_MANUFACTURER_NAME_STRING_CHARACTERISTIC, - Characteristic.READ, + Characteristic.Properties.READ, Characteristic.READABLE, 'Fitbit', - [descriptor] + [descriptor], + ) + device_info_service = Service( + GATT_DEVICE_INFORMATION_SERVICE, [manufacturer_name_characteristic] ) - device_info_service = Service(GATT_DEVICE_INFORMATION_SERVICE, [ - manufacturer_name_characteristic - ]) custom_service1 = Service( '50DB505C-8AC4-4738-8448-3B1D9CC09CC5', [ Characteristic( 'D901B45B-4916-412E-ACCA-376ECB603B2C', - Characteristic.READ | Characteristic.WRITE, + Characteristic.Properties.READ | Characteristic.Properties.WRITE, Characteristic.READABLE | Characteristic.WRITEABLE, - CharacteristicValue(read=my_custom_read, write=my_custom_write) + CharacteristicValue(read=my_custom_read, write=my_custom_write), ), Characteristic( '552957FB-CF1F-4A31-9535-E78847E1A714', - Characteristic.READ | Characteristic.WRITE, + Characteristic.Properties.READ | Characteristic.Properties.WRITE, Characteristic.READABLE | Characteristic.WRITEABLE, - CharacteristicValue(read=my_custom_read_with_error, write=my_custom_write_with_error) + CharacteristicValue( + read=my_custom_read_with_error, write=my_custom_write_with_error + ), ), Characteristic( '486F64C6-4B5F-4B3B-8AFF-EDE134A8446A', - Characteristic.READ | Characteristic.NOTIFY, + Characteristic.Properties.READ | Characteristic.Properties.NOTIFY, Characteristic.READABLE, - 'hello' - ) - ] + 'hello', + ), + ], ) device.add_services([device_info_service, custom_service1]) @@ -142,6 +148,7 @@ async def main(): await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_hfp_gateway.py b/examples/run_hfp_gateway.py index 69db0c1..63a2a7c 100644 --- a/examples/run_hfp_gateway.py +++ b/examples/run_hfp_gateway.py @@ -20,10 +20,16 @@ import sys import os import logging -from colors import color +from bumble.colors import color + +import bumble.core from bumble.device import Device from bumble.transport import open_transport_or_link -from bumble.core import ConnectionError, BT_BR_EDR_TRANSPORT +from bumble.core import ( + BT_HANDSFREE_SERVICE, + BT_RFCOMM_PROTOCOL_ID, + BT_BR_EDR_TRANSPORT, +) from bumble.rfcomm import Client from bumble.sdp import ( Client as SDP_Client, @@ -31,16 +37,13 @@ from bumble.sdp import ( ServiceAttribute, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, - SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID -) -from bumble.hci import ( - BT_HANDSFREE_SERVICE, - BT_RFCOMM_PROTOCOL_ID + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, ) from bumble.hfp import HfpProtocol # ----------------------------------------------------------------------------- +# pylint: disable-next=too-many-nested-blocks async def list_rfcomm_channels(device, connection): # Connect to the SDP Server sdp_client = SDP_Client(device) @@ -52,49 +55,72 @@ async def list_rfcomm_channels(device, connection): [ SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, - SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID - ] + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + ], ) print(color('==================================', 'blue')) print(color('Handsfree Services:', 'yellow')) rfcomm_channels = [] + # pylint: disable-next=too-many-nested-blocks for attribute_list in search_result: # Look for the RFCOMM Channel number protocol_descriptor_list = ServiceAttribute.find_attribute_in_list( - attribute_list, - SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID + attribute_list, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID ) if protocol_descriptor_list: for protocol_descriptor in protocol_descriptor_list.value: if len(protocol_descriptor.value) >= 2: if protocol_descriptor.value[0].value == BT_RFCOMM_PROTOCOL_ID: print(color('SERVICE:', 'green')) - print(color(' RFCOMM Channel:', 'cyan'), protocol_descriptor.value[1].value) + print( + color(' RFCOMM Channel:', 'cyan'), + protocol_descriptor.value[1].value, + ) rfcomm_channels.append(protocol_descriptor.value[1].value) # List profiles - bluetooth_profile_descriptor_list = ServiceAttribute.find_attribute_in_list( - attribute_list, - SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID + bluetooth_profile_descriptor_list = ( + ServiceAttribute.find_attribute_in_list( + attribute_list, + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, + ) ) if bluetooth_profile_descriptor_list: if bluetooth_profile_descriptor_list.value: - if bluetooth_profile_descriptor_list.value[0].type == DataElement.SEQUENCE: - bluetooth_profile_descriptors = bluetooth_profile_descriptor_list.value + if ( + bluetooth_profile_descriptor_list.value[0].type + == DataElement.SEQUENCE + ): + bluetooth_profile_descriptors = ( + bluetooth_profile_descriptor_list.value + ) else: - # Sometimes, instead of a list of lists, we just find a list. Fix that - bluetooth_profile_descriptors = [bluetooth_profile_descriptor_list] + # Sometimes, instead of a list of lists, we just + # find a list. Fix that + bluetooth_profile_descriptors = [ + bluetooth_profile_descriptor_list + ] print(color(' Profiles:', 'green')) - for bluetooth_profile_descriptor in bluetooth_profile_descriptors: - version_major = bluetooth_profile_descriptor.value[1].value >> 8 - version_minor = bluetooth_profile_descriptor.value[1].value & 0xFF - print(f' {bluetooth_profile_descriptor.value[0].value} - version {version_major}.{version_minor}') + for ( + bluetooth_profile_descriptor + ) in bluetooth_profile_descriptors: + version_major = ( + bluetooth_profile_descriptor.value[1].value >> 8 + ) + version_minor = ( + bluetooth_profile_descriptor.value[1].value + & 0xFF + ) + print( + ' ' + f'{bluetooth_profile_descriptor.value[0].value}' + f' - version {version_major}.{version_minor}' + ) # List service classes service_class_id_list = ServiceAttribute.find_attribute_in_list( - attribute_list, - SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID + attribute_list, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID ) if service_class_id_list: if service_class_id_list.value: @@ -109,9 +135,14 @@ async def list_rfcomm_channels(device, connection): # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 4: - print('Usage: run_hfp_gateway.py <device-config> <transport-spec> <bluetooth-address>') - print(' specifying a channel number, or "discover" to list all RFCOMM channels') - print('example: run_hfp_gateway.py hfp_gateway.json usb:04b4:f901 E1:CA:72:48:C4:E8') + print( + 'Usage: run_hfp_gateway.py <device-config> <transport-spec> ' + '<bluetooth-address>' + ) + print( + ' specifying a channel number, or "discover" to list all RFCOMM channels' + ) + print('example: run_hfp_gateway.py hfp_gateway.json usb:0 E1:CA:72:48:C4:E8') return print('<<< connecting to HCI...') @@ -158,7 +189,7 @@ async def main(): try: session = await rfcomm_mux.open_dlc(channel) print('### Session open', session) - except ConnectionError as error: + except bumble.core.ConnectionError as error: print(f'### Session open failed: {error}') await rfcomm_mux.disconnect() print('@@@ Disconnected from RFCOMM server') @@ -173,7 +204,11 @@ async def main(): protocol.send_response_line('+BRSF: 30') protocol.send_response_line('OK') elif line.startswith('AT+CIND=?'): - protocol.send_response_line('+CIND: ("call",(0,1)),("callsetup",(0-3)),("service",(0-1)),("signal",(0-5)),("roam",(0,1)),("battchg",(0-5)),("callheld",(0-2))') + protocol.send_response_line( + '+CIND: ("call",(0,1)),("callsetup",(0-3)),("service",(0-1)),' + '("signal",(0-5)),("roam",(0,1)),("battchg",(0-5)),' + '("callheld",(0-2))' + ) protocol.send_response_line('OK') elif line.startswith('AT+CIND?'): protocol.send_response_line('+CIND: 0,0,1,4,1,5,0') @@ -193,7 +228,9 @@ async def main(): elif line.startswith('AT+BIA='): protocol.send_response_line('OK') elif line.startswith('AT+BVRA='): - protocol.send_response_line('+BVRA: 1,1,12AA,1,1,"Message 1 from Janina"') + protocol.send_response_line( + '+BVRA: 1,1,12AA,1,1,"Message 1 from Janina"' + ) elif line.startswith('AT+XEVENT='): protocol.send_response_line('OK') elif line.startswith('AT+XAPL='): @@ -204,6 +241,7 @@ async def main(): await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_hfp_handsfree.py b/examples/run_hfp_handsfree.py index cf7a053..cef29c0 100644 --- a/examples/run_hfp_handsfree.py +++ b/examples/run_hfp_handsfree.py @@ -19,26 +19,26 @@ import asyncio import sys import os import logging -import websockets import json +import websockets from bumble.device import Device from bumble.transport import open_transport_or_link -from bumble.rfcomm import Server as RfommServer +from bumble.rfcomm import Server as RfcommServer from bumble.sdp import ( DataElement, ServiceAttribute, SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, - SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, ) from bumble.core import ( BT_GENERIC_AUDIO_SERVICE, BT_HANDSFREE_SERVICE, BT_L2CAP_PROTOCOL_ID, - BT_RFCOMM_PROTOCOL_ID + BT_RFCOMM_PROTOCOL_ID, ) from bumble.hfp import HfpProtocol @@ -49,36 +49,44 @@ def make_sdp_records(rfcomm_channel): 0x00010001: [ ServiceAttribute( SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, - DataElement.unsigned_integer_32(0x00010001) + DataElement.unsigned_integer_32(0x00010001), ), ServiceAttribute( SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, - DataElement.sequence([ - DataElement.uuid(BT_HANDSFREE_SERVICE), - DataElement.uuid(BT_GENERIC_AUDIO_SERVICE) - ]) + DataElement.sequence( + [ + DataElement.uuid(BT_HANDSFREE_SERVICE), + DataElement.uuid(BT_GENERIC_AUDIO_SERVICE), + ] + ), ), ServiceAttribute( SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, - DataElement.sequence([ - DataElement.sequence([ - DataElement.uuid(BT_L2CAP_PROTOCOL_ID) - ]), - DataElement.sequence([ - DataElement.uuid(BT_RFCOMM_PROTOCOL_ID), - DataElement.unsigned_integer_8(rfcomm_channel) - ]) - ]) + DataElement.sequence( + [ + DataElement.sequence([DataElement.uuid(BT_L2CAP_PROTOCOL_ID)]), + DataElement.sequence( + [ + DataElement.uuid(BT_RFCOMM_PROTOCOL_ID), + DataElement.unsigned_integer_8(rfcomm_channel), + ] + ), + ] + ), ), ServiceAttribute( SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, - DataElement.sequence([ - DataElement.sequence([ - DataElement.uuid(BT_HANDSFREE_SERVICE), - DataElement.unsigned_integer_16(0x0105) - ]) - ]) - ) + DataElement.sequence( + [ + DataElement.sequence( + [ + DataElement.uuid(BT_HANDSFREE_SERVICE), + DataElement.unsigned_integer_16(0x0105), + ] + ) + ] + ), + ), ] } @@ -89,7 +97,7 @@ class UiServer: async def start(self): # Start a Websocket server to receive events from a web page - async def serve(websocket, path): + async def serve(websocket, _path): while True: try: message = await websocket.recv() @@ -103,6 +111,8 @@ class UiServer: except websockets.exceptions.ConnectionClosedOK: pass + + # pylint: disable=no-member await websockets.serve(serve, 'localhost', 8989) @@ -111,7 +121,7 @@ async def protocol_loop(protocol): await protocol.initialize_service() while True: - await(protocol.next_line()) + await (protocol.next_line()) # ----------------------------------------------------------------------------- @@ -138,7 +148,7 @@ async def main(): device.classic_enabled = True # Create and register a server - rfcomm_server = RfommServer(device) + rfcomm_server = RfcommServer(device) # Listen for incoming DLC connections channel_number = rfcomm_server.listen(on_dlc) @@ -160,6 +170,7 @@ async def main(): await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_notifier.py b/examples/run_notifier.py index f52b56c..5f6def3 100644 --- a/examples/run_notifier.py +++ b/examples/run_notifier.py @@ -23,10 +23,7 @@ import logging from bumble.device import Device, Connection from bumble.transport import open_transport_or_link -from bumble.gatt import ( - Service, - Characteristic -) +from bumble.gatt import Service, Characteristic # ----------------------------------------------------------------------------- @@ -41,19 +38,29 @@ class Listener(Device.Listener, Connection.Listener): def on_disconnection(self, reason): print(f'### Disconnected, reason={reason}') - def on_characteristic_subscription(self, connection, characteristic, notify_enabled, indicate_enabled): + def on_characteristic_subscription( + self, connection, characteristic, notify_enabled, indicate_enabled + ): print( - f'$$$ Characteristic subscription for handle {characteristic.handle} from {connection}: ' + f'$$$ Characteristic subscription for handle {characteristic.handle} ' + f'from {connection}: ' f'notify {"enabled" if notify_enabled else "disabled"}, ' f'indicate {"enabled" if indicate_enabled else "disabled"}' ) # ----------------------------------------------------------------------------- +# Alternative way to listen for subscriptions +# ----------------------------------------------------------------------------- +def on_my_characteristic_subscription(peer, enabled): + print(f'### My characteristic from {peer}: {"enabled" if enabled else "disabled"}') + + +# ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 3: - print('Usage: run_gatt_server.py <device-config> <transport-spec>') - print('example: run_gatt_server.py device1.json usb:0') + print('Usage: run_notifier.py <device-config> <transport-spec>') + print('example: run_notifier.py device1.json usb:0') return print('<<< connecting to HCI...') @@ -67,25 +74,28 @@ async def main(): # Add a few entries to the device's GATT server characteristic1 = Characteristic( '486F64C6-4B5F-4B3B-8AFF-EDE134A8446A', - Characteristic.READ | Characteristic.NOTIFY, + Characteristic.Properties.READ | Characteristic.Properties.NOTIFY, Characteristic.READABLE, - bytes([0x40]) + bytes([0x40]), ) characteristic2 = Characteristic( '8EBDEBAE-0017-418E-8D3B-3A3809492165', - Characteristic.READ | Characteristic.INDICATE, + Characteristic.Properties.READ | Characteristic.Properties.INDICATE, Characteristic.READABLE, - bytes([0x41]) + bytes([0x41]), ) characteristic3 = Characteristic( '8EBDEBAE-0017-418E-8D3B-3A3809492165', - Characteristic.READ | Characteristic.NOTIFY | Characteristic.INDICATE, + Characteristic.Properties.READ + | Characteristic.Properties.NOTIFY + | Characteristic.Properties.INDICATE, Characteristic.READABLE, - bytes([0x42]) + bytes([0x42]), ) + characteristic3.on('subscription', on_my_characteristic_subscription) custom_service = Service( '50DB505C-8AC4-4738-8448-3B1D9CC09CC5', - [characteristic1, characteristic2, characteristic3] + [characteristic1, characteristic2, characteristic3], ) device.add_services([custom_service]) @@ -116,5 +126,5 @@ async def main(): # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_rfcomm_client.py b/examples/run_rfcomm_client.py index 83ef848..9a94278 100644 --- a/examples/run_rfcomm_client.py +++ b/examples/run_rfcomm_client.py @@ -20,10 +20,16 @@ import sys import os import logging -from colors import color +from bumble.colors import color + +import bumble.core from bumble.device import Device from bumble.transport import open_transport_or_link -from bumble.core import ConnectionError, BT_BR_EDR_TRANSPORT +from bumble.core import ( + BT_L2CAP_PROTOCOL_ID, + BT_RFCOMM_PROTOCOL_ID, + BT_BR_EDR_TRANSPORT, +) from bumble.rfcomm import Client from bumble.sdp import ( Client as SDP_Client, @@ -31,9 +37,8 @@ from bumble.sdp import ( ServiceAttribute, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, - SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, ) -from bumble.hci import BT_L2CAP_PROTOCOL_ID, BT_RFCOMM_PROTOCOL_ID # ----------------------------------------------------------------------------- @@ -48,47 +53,70 @@ async def list_rfcomm_channels(device, connection): [ SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, - SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID - ] + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + ], ) print(color('==================================', 'blue')) print(color('RFCOMM Services:', 'yellow')) + # pylint: disable-next=too-many-nested-blocks for attribute_list in search_result: # Look for the RFCOMM Channel number protocol_descriptor_list = ServiceAttribute.find_attribute_in_list( - attribute_list, - SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID + attribute_list, SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID ) if protocol_descriptor_list: for protocol_descriptor in protocol_descriptor_list.value: if len(protocol_descriptor.value) >= 2: if protocol_descriptor.value[0].value == BT_RFCOMM_PROTOCOL_ID: print(color('SERVICE:', 'green')) - print(color(' RFCOMM Channel:', 'cyan'), protocol_descriptor.value[1].value) + print( + color(' RFCOMM Channel:', 'cyan'), + protocol_descriptor.value[1].value, + ) # List profiles - bluetooth_profile_descriptor_list = ServiceAttribute.find_attribute_in_list( - attribute_list, - SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID + bluetooth_profile_descriptor_list = ( + ServiceAttribute.find_attribute_in_list( + attribute_list, + SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID, + ) ) if bluetooth_profile_descriptor_list: if bluetooth_profile_descriptor_list.value: - if bluetooth_profile_descriptor_list.value[0].type == DataElement.SEQUENCE: - bluetooth_profile_descriptors = bluetooth_profile_descriptor_list.value + if ( + bluetooth_profile_descriptor_list.value[0].type + == DataElement.SEQUENCE + ): + bluetooth_profile_descriptors = ( + bluetooth_profile_descriptor_list.value + ) else: - # Sometimes, instead of a list of lists, we just find a list. Fix that - bluetooth_profile_descriptors = [bluetooth_profile_descriptor_list] + # Sometimes, instead of a list of lists, we just + # find a list. Fix that + bluetooth_profile_descriptors = [ + bluetooth_profile_descriptor_list + ] print(color(' Profiles:', 'green')) - for bluetooth_profile_descriptor in bluetooth_profile_descriptors: - version_major = bluetooth_profile_descriptor.value[1].value >> 8 - version_minor = bluetooth_profile_descriptor.value[1].value & 0xFF - print(f' {bluetooth_profile_descriptor.value[0].value} - version {version_major}.{version_minor}') + for ( + bluetooth_profile_descriptor + ) in bluetooth_profile_descriptors: + version_major = ( + bluetooth_profile_descriptor.value[1].value >> 8 + ) + version_minor = ( + bluetooth_profile_descriptor.value[1].value + & 0xFF + ) + print( + ' ' + f'{bluetooth_profile_descriptor.value[0].value}' + f' - version {version_major}.{version_minor}' + ) # List service classes service_class_id_list = ServiceAttribute.find_attribute_in_list( - attribute_list, - SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID + attribute_list, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID ) if service_class_id_list: if service_class_id_list.value: @@ -98,14 +126,16 @@ async def list_rfcomm_channels(device, connection): await sdp_client.disconnect() + # ----------------------------------------------------------------------------- class TcpServerProtocol(asyncio.Protocol): def __init__(self, rfcomm_session): self.rfcomm_session = rfcomm_session + self.transport = None def connection_made(self, transport): - peername = transport.get_extra_info('peername') - print(f'<<< TCP Server: connection from {peername}') + peer_name = transport.get_extra_info('peer_name') + print(f'<<< TCP Server: connection from {peer_name}') self.transport = transport self.rfcomm_session.sink = self.rfcomm_data_received @@ -137,9 +167,14 @@ async def tcp_server(tcp_port, rfcomm_session): # ----------------------------------------------------------------------------- async def main(): if len(sys.argv) < 5: - print('Usage: run_rfcomm_client.py <device-config> <transport-spec> <bluetooth-address> <channel>|discover [tcp-port]') - print(' specifying a channel number, or "discover" to list all RFCOMM channels') - print('example: run_rfcomm_client.py classic1.json usb:04b4:f901 E1:CA:72:48:C4:E8 8') + print( + 'Usage: run_rfcomm_client.py <device-config> <transport-spec> ' + '<bluetooth-address> <channel>|discover [tcp-port]' + ) + print( + ' specifying a channel number, or "discover" to list all RFCOMM channels' + ) + print('example: run_rfcomm_client.py classic1.json usb:0 E1:CA:72:48:C4:E8 8') return print('<<< connecting to HCI...') @@ -173,7 +208,7 @@ async def main(): print('*** Encryption on') # Create a client and start it - print('@@@ Starting to RFCOMM client...') + print('@@@ Starting RFCOMM client...') rfcomm_client = Client(device, connection) rfcomm_mux = await rfcomm_client.start() print('@@@ Started') @@ -183,7 +218,7 @@ async def main(): try: session = await rfcomm_mux.open_dlc(channel) print('### Session open', session) - except ConnectionError as error: + except bumble.core.ConnectionError as error: print(f'### Session open failed: {error}') await rfcomm_mux.disconnect() print('@@@ Disconnected from RFCOMM server') @@ -192,10 +227,11 @@ async def main(): if len(sys.argv) == 6: # A TCP port was specified, start listening tcp_port = int(sys.argv[5]) - asyncio.get_running_loop().create_task(tcp_server(tcp_port, session)) + asyncio.create_task(tcp_server(tcp_port, session)) await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_rfcomm_server.py b/examples/run_rfcomm_server.py index a239ceb..71feca9 100644 --- a/examples/run_rfcomm_server.py +++ b/examples/run_rfcomm_server.py @@ -22,7 +22,7 @@ import logging from bumble.device import Device from bumble.transport import open_transport_or_link -from bumble.core import UUID +from bumble.core import BT_L2CAP_PROTOCOL_ID, BT_RFCOMM_PROTOCOL_ID, UUID from bumble.rfcomm import Server from bumble.sdp import ( DataElement, @@ -31,31 +31,42 @@ from bumble.sdp import ( SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, - SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID + SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, ) -from bumble.hci import BT_L2CAP_PROTOCOL_ID, BT_RFCOMM_PROTOCOL_ID # ----------------------------------------------------------------------------- def sdp_records(channel): return { 0x00010001: [ - ServiceAttribute(SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, DataElement.unsigned_integer_32(0x00010001)), - ServiceAttribute(SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT) - ])), - ServiceAttribute(SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.uuid(UUID('E6D55659-C8B4-4B85-96BB-B1143AF6D3AE')) - ])), - ServiceAttribute(SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, DataElement.sequence([ - DataElement.sequence([ - DataElement.uuid(BT_L2CAP_PROTOCOL_ID) - ]), - DataElement.sequence([ - DataElement.uuid(BT_RFCOMM_PROTOCOL_ID), - DataElement.unsigned_integer_8(channel) - ]) - ])) + ServiceAttribute( + SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID, + DataElement.unsigned_integer_32(0x00010001), + ), + ServiceAttribute( + SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID, + DataElement.sequence([DataElement.uuid(SDP_PUBLIC_BROWSE_ROOT)]), + ), + ServiceAttribute( + SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [DataElement.uuid(UUID('E6D55659-C8B4-4B85-96BB-B1143AF6D3AE'))] + ), + ), + ServiceAttribute( + SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID, + DataElement.sequence( + [ + DataElement.sequence([DataElement.uuid(BT_L2CAP_PROTOCOL_ID)]), + DataElement.sequence( + [ + DataElement.uuid(BT_RFCOMM_PROTOCOL_ID), + DataElement.unsigned_integer_8(channel), + ] + ), + ] + ), + ), ] } @@ -113,6 +124,7 @@ async def main(): await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/examples/run_scanner.py b/examples/run_scanner.py index feed88f..bdd7fba 100644 --- a/examples/run_scanner.py +++ b/examples/run_scanner.py @@ -19,7 +19,7 @@ import asyncio import sys import os import logging -from colors import color +from bumble.colors import color from bumble.device import Device from bumble.transport import open_transport_or_link @@ -35,35 +35,44 @@ async def main(): print('<<< connecting to HCI...') async with await open_transport_or_link(sys.argv[1]) as (hci_source, hci_sink): print('<<< connected') - filter_duplicates = (len(sys.argv) == 3 and sys.argv[2] == 'filter') + filter_duplicates = len(sys.argv) == 3 and sys.argv[2] == 'filter' device = Device.with_hci('Bumble', 'F0:F1:F2:F3:F4:F5', hci_source, hci_sink) @device.on('advertisement') - def _(address, ad_data, rssi, connectable): - address_type_string = ('PUBLIC', 'RANDOM', 'PUBLIC_ID', 'RANDOM_ID')[address.address_type] - address_color = 'yellow' if connectable else 'red' + def _(advertisement): + address_type_string = ('PUBLIC', 'RANDOM', 'PUBLIC_ID', 'RANDOM_ID')[ + advertisement.address.address_type + ] + address_color = 'yellow' if advertisement.is_connectable else 'red' address_qualifier = '' if address_type_string.startswith('P'): type_color = 'cyan' else: - if address.is_static: + if advertisement.address.is_static: type_color = 'green' address_qualifier = '(static)' - elif address.is_resolvable: + elif advertisement.address.is_resolvable: type_color = 'magenta' address_qualifier = '(resolvable)' else: type_color = 'white' separator = '\n ' - print(f'>>> {color(address, address_color)} [{color(address_type_string, type_color)}]{address_qualifier}:{separator}RSSI:{rssi}{separator}{ad_data.to_string(separator)}') + print( + f'>>> {color(advertisement.address, address_color)} ' + f'[{color(address_type_string, type_color)}]' + f'{address_qualifier}:{separator}RSSI:{advertisement.rssi}' + f'{separator}' + f'{advertisement.data.to_string(separator)}' + ) await device.power_on() await device.start_scanning(filter_duplicates=filter_duplicates) await hci_source.wait_for_termination() + # ----------------------------------------------------------------------------- -logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) +logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper()) asyncio.run(main()) diff --git a/pyproject.toml b/pyproject.toml index 6eca00c..8662723 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,3 +3,85 @@ requires = ["setuptools>=52", "wheel", "setuptools_scm>=6.2"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] +write_to = "bumble/_version.py" + +[tool.pytest.ini_options] +pythonpath = "." +testpaths = [ + "tests" +] + +[tool.pylint.master] +init-hook = 'import sys; sys.path.append(".")' +ignore-paths = [ + '.*_pb2(_grpc)?.py' +] + +[tool.pylint.messages_control] +max-line-length = "88" + +disable = [ + "broad-except", + "fixme", + "logging-fstring-interpolation", + "logging-not-lazy", + "no-member", # Temporary until pylint works better with class/method decorators + "no-value-for-parameter", # Temporary until pylint works better with class/method decorators + "missing-class-docstring", + "missing-function-docstring", + "missing-module-docstring", + "too-few-public-methods", + "too-many-arguments", + "too-many-branches", + "too-many-instance-attributes", + "too-many-lines", + "too-many-locals", + "too-many-public-methods", + "too-many-statements", +] + +[tool.pylint.main] +ignore="pandora" # FIXME: pylint does not support stubs yet: + +[tool.pylint.typecheck] +signature-mutators="AsyncRunner.run_in_task" + +[tool.black] +skip-string-normalization = true +extend-exclude = ''' +( + .*_pb2(_grpc)?.py # exclude autogenerated Protocol Buffer files anywhere in the project +) +''' + +[tool.mypy] +exclude = ['bumble/transport/grpc_protobuf'] + +[[tool.mypy.overrides]] +module = "bumble.transport.grpc_protobuf.*" +ignore_errors = true + +[[tool.mypy.overrides]] +module = "aioconsole.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "construct.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "grpc.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "serial_asyncio.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "usb.*" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "usb1.*" +ignore_missing_imports = true + diff --git a/scripts/process_android_emulator_protos.sh b/scripts/process_android_emulator_protos.sh new file mode 100644 index 0000000..d5070df --- /dev/null +++ b/scripts/process_android_emulator_protos.sh @@ -0,0 +1,16 @@ +# Invoke this script with an argument pointing to where the Android emulator .proto files are +# (for example, ~/Library/Android/sdk/emulator/lib on a mac, or +# $AOSP/external/qemu/android/android-grpc/python/aemu-grpc/src/aemu/proto from the AOSP sources) +PROTOC_OUT=bumble/transport/grpc_protobuf + +proto_files=(emulated_bluetooth.proto emulated_bluetooth_vhci.proto emulated_bluetooth_packets.proto emulated_bluetooth_device.proto grpc_endpoint_description.proto) +for proto_file in "${proto_files[@]}" +do + python -m grpc_tools.protoc -I$1 --proto_path=bumble/transport --python_out=$PROTOC_OUT --pyi_out=$PROTOC_OUT --grpc_python_out=$PROTOC_OUT $1/$proto_file +done + +python_files=(emulated_bluetooth_pb2_grpc.py emulated_bluetooth_pb2.py emulated_bluetooth_packets_pb2.py emulated_bluetooth_vhci_pb2_grpc.py emulated_bluetooth_vhci_pb2.py emulated_bluetooth_device_pb2.py grpc_endpoint_description_pb2.py) +for python_file in "${python_files[@]}" +do + sed -i 's/^import .*_pb2 as/from . \0/' $PROTOC_OUT/$python_file +done
\ No newline at end of file diff --git a/scripts/process_android_netsim_protos.sh b/scripts/process_android_netsim_protos.sh new file mode 100644 index 0000000..5a26602 --- /dev/null +++ b/scripts/process_android_netsim_protos.sh @@ -0,0 +1,14 @@ +# Invoke this script with an argument pointing to where the AOSP `tools/netsim/src/proto` is +PROTOC_OUT=bumble/transport/grpc_protobuf + +proto_files=(common.proto packet_streamer.proto hci_packet.proto startup.proto) +for proto_file in "${proto_files[@]}" +do + python -m grpc_tools.protoc -I$1 --proto_path=bumble/transport --python_out=$PROTOC_OUT --pyi_out=$PROTOC_OUT --grpc_python_out=$PROTOC_OUT $1/$proto_file +done + +python_files=(packet_streamer_pb2_grpc.py packet_streamer_pb2.py hci_packet_pb2.py startup_pb2.py) +for python_file in "${python_files[@]}" +do + sed -i 's/^import .*_pb2 as/from . \0/' $PROTOC_OUT/$python_file +done @@ -16,25 +16,26 @@ name = bumble use_scm_version = True description = Bluetooth Stack for Apps, Emulation, Test and Experimentation +long_description = file: README.md +long_description_content_type = text/markdown author = Google author_email = tbd@tbd.com url = https://github.com/google/bumble [options] python_requires = >=3.8 -packages = bumble, bumble.transport, bumble.apps, bumble.apps.link_relay +packages = bumble, bumble.transport, bumble.profiles, bumble.apps, bumble.apps.link_relay, bumble.pandora package_dir = bumble = bumble bumble.apps = apps +include-package-data = True install_requires = - aioconsole >= 0.4.1 - ansicolors >= 1.1 appdirs >= 1.4 - bitstruct >= 8.12 click >= 7.1.2; platform_system!='Emscripten' cryptography == 35; platform_system!='Emscripten' - grpcio >= 1.46; platform_system!='Emscripten' + grpcio == 1.51.1; platform_system!='Emscripten' libusb1 >= 2.0.1; platform_system!='Emscripten' + libusb-package == 1.0.26.1; platform_system!='Emscripten' prompt_toolkit >= 3.0.16; platform_system!='Emscripten' protobuf >= 3.12.4 pyee >= 8.2.2 @@ -42,27 +43,48 @@ install_requires = pyserial >= 3.5; platform_system!='Emscripten' pyusb >= 1.2; platform_system!='Emscripten' websockets >= 8.1; platform_system!='Emscripten' + prettytable >= 3.6.0 + humanize >= 4.6.0 + bt-test-interfaces >= 0.0.2 [options.entry_points] console_scripts = bumble-console = bumble.apps.console:main + bumble-controller-info = bumble.apps.controller_info:main bumble-gatt-dump = bumble.apps.gatt_dump:main bumble-hci-bridge = bumble.apps.hci_bridge:main + bumble-l2cap-bridge = bumble.apps.l2cap_bridge:main bumble-pair = bumble.apps.pair:main bumble-scan = bumble.apps.scan:main bumble-show = bumble.apps.show:main bumble-unbond = bumble.apps.unbond:main + bumble-usb-probe = bumble.apps.usb_probe:main bumble-link-relay = bumble.apps.link_relay.link_relay:main + bumble-bench = bumble.apps.bench:main + bumble-pandora-server = bumble.apps.pandora_server:main + +[options.package_data] +* = py.typed, *.pyi [options.extras_require] +build = + build >= 0.7 test = pytest >= 6.2 pytest-asyncio >= 0.17 + pytest-html >= 3.2.0 + coverage >= 6.4 development = - invoke >= 1.4 - build >= 0.7 + black == 22.10 + grpcio-tools >= 1.51.1 + invoke >= 1.7.3 + mypy == 1.2.0 nox >= 2022 + pylint == 2.15.8 + types-appdirs >= 1.4.3 + types-invoke >= 1.7.3 + types-protobuf >= 4.21.0 documentation = - mkdocs >= 1.2.3 - mkdocs-material >= 8.1.9 + mkdocs >= 1.4.0 + mkdocs-material >= 8.5.6 mkdocstrings[python] >= 0.19.0 @@ -13,4 +13,5 @@ # limitations under the License. from setuptools import setup + setup() @@ -15,43 +15,165 @@ """ Invoke tasks """ + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- import os -from invoke import task, Collection +from invoke import task, call, Collection +from invoke.exceptions import Exit, UnexpectedExit + +# ----------------------------------------------------------------------------- ROOT_DIR = os.path.dirname(os.path.realpath(__file__)) ns = Collection() + +# ----------------------------------------------------------------------------- +# Build +# ----------------------------------------------------------------------------- build_tasks = Collection() -ns.add_collection(build_tasks, name='build') +ns.add_collection(build_tasks, name="build") +# ----------------------------------------------------------------------------- @task -def build(ctx): - ctx.run('python -m build') +def build(ctx, install=False): + if install: + ctx.run('python -m pip install .[build]') -build_tasks.add_task(build, default=True, name='build') + ctx.run("python -m build") -test_tasks = Collection() -ns.add_collection(test_tasks, name='test') +# ----------------------------------------------------------------------------- @task -def test(ctx, filter=None, junit=False): +def release_build(ctx): + build(ctx, install=True) + + +# ----------------------------------------------------------------------------- +@task +def mkdocs(ctx): + ctx.run("mkdocs build -f docs/mkdocs/mkdocs.yml") + + +# ----------------------------------------------------------------------------- +build_tasks.add_task(build, default=True) +build_tasks.add_task(release_build, name="release") +build_tasks.add_task(mkdocs, name="mkdocs") + + +# ----------------------------------------------------------------------------- +# Test +# ----------------------------------------------------------------------------- +test_tasks = Collection() +ns.add_collection(test_tasks, name="test") + + +# ----------------------------------------------------------------------------- +@task(incrementable=["verbose"]) +def test(ctx, match=None, junit=False, install=False, html=False, verbose=0): + # Install the package before running the tests + if install: + ctx.run("python -m pip install .[test]") + args = "" if junit: args += "--junit-xml test-results.xml" - if filter is not None: - args += " -k '{}'".format(filter) - ctx.run('python -m pytest {} {}' - .format(os.path.join(ROOT_DIR, "tests"), args)) + if match is not None: + args += f" -k '{match}'" + if html: + args += " --html results.html" + if verbose > 0: + args += f" -{'v' * verbose}" + ctx.run(f"python -m pytest {os.path.join(ROOT_DIR, 'tests')} {args}") -test_tasks.add_task(test, name='test', default=True) +# ----------------------------------------------------------------------------- +@task +def release_test(ctx): + test(ctx, install=True) + +# ----------------------------------------------------------------------------- +test_tasks.add_task(test, default=True) +test_tasks.add_task(release_test, name="release") + +# ----------------------------------------------------------------------------- +# Project +# ----------------------------------------------------------------------------- +project_tasks = Collection() +ns.add_collection(project_tasks, name="project") + + +# ----------------------------------------------------------------------------- @task -def mkdocs(ctx): - ctx.run('mkdocs build -f docs/mkdocs/mkdocs.yml') +def lint(ctx, disable='C,R', errors_only=False): + options = [] + if disable: + options.append(f"--disable={disable}") + if errors_only: + options.append("-E") + + if errors_only: + qualifier = ' (errors only)' + else: + qualifier = f' (disabled: {disable})' if disable else '' + + print(f">>> Running the linter{qualifier}...") + try: + ctx.run(f"pylint {' '.join(options)} bumble apps examples tasks.py") + print("The linter is happy. ✅ 😊 ðŸ'") + except UnexpectedExit as exc: + print("Please check your code against the linter messages. âŒ") + raise Exit(code=1) from exc + + +# ----------------------------------------------------------------------------- +@task +def format_code(ctx, check=False, diff=False): + options = [] + if check: + options.append("--check") + if diff: + options.append("--diff") + + print(">>> Running the formatter...") + try: + ctx.run(f"black -S {' '.join(options)} .") + except UnexpectedExit as exc: + print("Please run 'invoke project.format' or 'black .' to format the code. âŒ") + raise Exit(code=1) from exc + + +# ----------------------------------------------------------------------------- +@task +def check_types(ctx): + checklist = ["apps", "bumble", "examples", "tests", "tasks.py"] + try: + ctx.run(f"mypy {' '.join(checklist)}") + except UnexpectedExit as exc: + print("Please check your code against the mypy messages.") + raise Exit(code=1) from exc + + +# ----------------------------------------------------------------------------- +@task( + pre=[ + call(format_code, check=True), + call(lint, errors_only=True), + call(check_types), + test, + ] +) +def pre_commit(_ctx): + print("All good!") -ns.add_task(mkdocs) +# ----------------------------------------------------------------------------- +project_tasks.add_task(lint) +project_tasks.add_task(format_code, name="format") +project_tasks.add_task(check_types, name="check-types") +project_tasks.add_task(pre_commit) diff --git a/tests/a2dp_test.py b/tests/a2dp_test.py index e09694d..92f7915 100644 --- a/tests/a2dp_test.py +++ b/tests/a2dp_test.py @@ -21,6 +21,7 @@ import os import pytest from bumble.controller import Controller +from bumble.core import BT_BR_EDR_TRANSPORT from bumble.link import LocalLink from bumble.device import Device from bumble.host import Host @@ -35,7 +36,7 @@ from bumble.avdtp import ( MediaPacket, AVDTP_AUDIO_MEDIA_TYPE, AVDTP_TSEP_SNK, - A2DP_SBC_CODEC_TYPE + A2DP_SBC_CODEC_TYPE, ) from bumble.a2dp import ( SbcMediaCodecInformation, @@ -44,7 +45,7 @@ from bumble.a2dp import ( SBC_STEREO_CHANNEL_MODE, SBC_JOINT_STEREO_CHANNEL_MODE, SBC_LOUDNESS_ALLOCATION_METHOD, - SBC_SNR_ALLOCATION_METHOD + SBC_SNR_ALLOCATION_METHOD, ) # ----------------------------------------------------------------------------- @@ -58,20 +59,21 @@ class TwoDevices: def __init__(self): self.connections = [None, None] + addresses = ['F0:F1:F2:F3:F4:F5', 'F5:F4:F3:F2:F1:F0'] self.link = LocalLink() self.controllers = [ - Controller('C1', link = self.link), - Controller('C2', link = self.link) + Controller('C1', link=self.link, public_address=addresses[0]), + Controller('C2', link=self.link, public_address=addresses[1]), ] self.devices = [ Device( - address = 'F0:F1:F2:F3:F4:F5', - host = Host(self.controllers[0], AsyncPipeSink(self.controllers[0])) + address=addresses[0], + host=Host(self.controllers[0], AsyncPipeSink(self.controllers[0])), ), Device( - address = 'F5:F4:F3:F2:F1:F0', - host = Host(self.controllers[1], AsyncPipeSink(self.controllers[1])) - ) + address=addresses[1], + host=Host(self.controllers[1], AsyncPipeSink(self.controllers[1])), + ), ] self.paired = [None, None] @@ -79,6 +81,9 @@ class TwoDevices: def on_connection(self, which, connection): self.connections[which] = connection + def on_paired(self, which, keys): + self.paired[which] = keys + # ----------------------------------------------------------------------------- @pytest.mark.asyncio @@ -87,57 +92,73 @@ async def test_self_connection(): two_devices = TwoDevices() # Attach listeners - two_devices.devices[0].on('connection', lambda connection: two_devices.on_connection(0, connection)) - two_devices.devices[1].on('connection', lambda connection: two_devices.on_connection(1, connection)) + two_devices.devices[0].on( + 'connection', lambda connection: two_devices.on_connection(0, connection) + ) + two_devices.devices[1].on( + 'connection', lambda connection: two_devices.on_connection(1, connection) + ) + + # Enable Classic connections + two_devices.devices[0].classic_enabled = True + two_devices.devices[1].classic_enabled = True # Start await two_devices.devices[0].power_on() await two_devices.devices[1].power_on() # Connect the two devices - await two_devices.devices[0].connect(two_devices.devices[1].random_address) + await asyncio.gather( + two_devices.devices[0].connect( + two_devices.devices[1].public_address, transport=BT_BR_EDR_TRANSPORT + ), + two_devices.devices[1].accept(two_devices.devices[0].public_address), + ) # Check the post conditions - assert(two_devices.connections[0] is not None) - assert(two_devices.connections[1] is not None) + assert two_devices.connections[0] is not None + assert two_devices.connections[1] is not None # ----------------------------------------------------------------------------- def source_codec_capabilities(): return MediaCodecCapabilities( - media_type = AVDTP_AUDIO_MEDIA_TYPE, - media_codec_type = A2DP_SBC_CODEC_TYPE, - media_codec_information = SbcMediaCodecInformation.from_discrete_values( - sampling_frequency = 44100, - channel_mode = SBC_JOINT_STEREO_CHANNEL_MODE, - block_length = 16, - subbands = 8, - allocation_method = SBC_LOUDNESS_ALLOCATION_METHOD, - minimum_bitpool_value = 2, - maximum_bitpool_value = 53 - ) + media_type=AVDTP_AUDIO_MEDIA_TYPE, + media_codec_type=A2DP_SBC_CODEC_TYPE, + media_codec_information=SbcMediaCodecInformation.from_discrete_values( + sampling_frequency=44100, + channel_mode=SBC_JOINT_STEREO_CHANNEL_MODE, + block_length=16, + subbands=8, + allocation_method=SBC_LOUDNESS_ALLOCATION_METHOD, + minimum_bitpool_value=2, + maximum_bitpool_value=53, + ), ) # ----------------------------------------------------------------------------- def sink_codec_capabilities(): return MediaCodecCapabilities( - media_type = AVDTP_AUDIO_MEDIA_TYPE, - media_codec_type = A2DP_SBC_CODEC_TYPE, - media_codec_information = SbcMediaCodecInformation.from_lists( - sampling_frequencies = [48000, 44100, 32000, 16000], - channel_modes = [ + media_type=AVDTP_AUDIO_MEDIA_TYPE, + media_codec_type=A2DP_SBC_CODEC_TYPE, + media_codec_information=SbcMediaCodecInformation.from_lists( + sampling_frequencies=[48000, 44100, 32000, 16000], + channel_modes=[ SBC_MONO_CHANNEL_MODE, SBC_DUAL_CHANNEL_MODE, SBC_STEREO_CHANNEL_MODE, - SBC_JOINT_STEREO_CHANNEL_MODE + SBC_JOINT_STEREO_CHANNEL_MODE, ], - block_lengths = [4, 8, 12, 16], - subbands = [4, 8], - allocation_methods = [SBC_LOUDNESS_ALLOCATION_METHOD, SBC_SNR_ALLOCATION_METHOD], - minimum_bitpool_value = 2, - maximum_bitpool_value = 53 - ) + block_lengths=[4, 8, 12, 16], + subbands=[4, 8], + allocation_methods=[ + SBC_LOUDNESS_ALLOCATION_METHOD, + SBC_SNR_ALLOCATION_METHOD, + ], + minimum_bitpool_value=2, + maximum_bitpool_value=53, + ), ) @@ -145,6 +166,9 @@ def sink_codec_capabilities(): @pytest.mark.asyncio async def test_source_sink_1(): two_devices = TwoDevices() + # Enable Classic connections + two_devices.devices[0].classic_enabled = True + two_devices.devices[1].classic_enabled = True await two_devices.devices[0].power_on() await two_devices.devices[1].power_on() @@ -164,21 +188,32 @@ async def test_source_sink_1(): listener = Listener(Listener.create_registrar(two_devices.devices[1])) listener.on('connection', on_avdtp_connection) - connection = await two_devices.devices[0].connect(two_devices.devices[1].random_address) + async def make_connection(): + connections = await asyncio.gather( + two_devices.devices[0].connect( + two_devices.devices[1].public_address, BT_BR_EDR_TRANSPORT + ), + two_devices.devices[1].accept(two_devices.devices[0].public_address), + ) + return connections[0] + + connection = await make_connection() client = await Protocol.connect(connection) endpoints = await client.discover_remote_endpoints() - assert(len(endpoints) == 1) + assert len(endpoints) == 1 remote_sink = list(endpoints)[0] - assert(remote_sink.in_use == 0) - assert(remote_sink.media_type == AVDTP_AUDIO_MEDIA_TYPE) - assert(remote_sink.tsep == AVDTP_TSEP_SNK) + assert remote_sink.in_use == 0 + assert remote_sink.media_type == AVDTP_AUDIO_MEDIA_TYPE + assert remote_sink.tsep == AVDTP_TSEP_SNK async def generate_packets(packet_count): sequence_number = 0 timestamp = 0 for i in range(packet_count): payload = bytes([sequence_number % 256]) - packet = MediaPacket(2, 0, 0, 0, sequence_number, timestamp, 0, [], 96, payload) + packet = MediaPacket( + 2, 0, 0, 0, sequence_number, timestamp, 0, [], 96, payload + ) packet.timestamp_seconds = timestamp / 44100 timestamp += 10 sequence_number += 1 @@ -192,50 +227,49 @@ async def test_source_sink_1(): source = client.add_source(source_codec_capabilities(), pump) stream = await client.create_stream(source, remote_sink) await stream.start() - assert(stream.state == AVDTP_STREAMING_STATE) - assert(stream.local_endpoint.in_use == 1) - assert(stream.rtp_channel is not None) - assert(sink.in_use == 1) - assert(sink.stream is not None) - assert(sink.stream.state == AVDTP_STREAMING_STATE) + assert stream.state == AVDTP_STREAMING_STATE + assert stream.local_endpoint.in_use == 1 + assert stream.rtp_channel is not None + assert sink.in_use == 1 + assert sink.stream is not None + assert sink.stream.state == AVDTP_STREAMING_STATE await rtp_packets_fully_received await stream.close() - assert(stream.rtp_channel is None) - assert(source.in_use == 0) - assert(source.stream.state == AVDTP_IDLE_STATE) - assert(sink.in_use == 0) - assert(sink.stream.state == AVDTP_IDLE_STATE) + assert stream.rtp_channel is None + assert source.in_use == 0 + assert source.stream.state == AVDTP_IDLE_STATE + assert sink.in_use == 0 + assert sink.stream.state == AVDTP_IDLE_STATE # Send packets manually rtp_packets_fully_received = asyncio.get_running_loop().create_future() rtp_packets_expected = 3 rtp_packets = [] source_packets = [ - MediaPacket(2, 0, 0, 0, i, i * 10, 0, [], 96, bytes([i])) - for i in range(3) + MediaPacket(2, 0, 0, 0, i, i * 10, 0, [], 96, bytes([i])) for i in range(3) ] source = client.add_source(source_codec_capabilities(), None) stream = await client.create_stream(source, remote_sink) await stream.start() - assert(stream.state == AVDTP_STREAMING_STATE) - assert(stream.local_endpoint.in_use == 1) - assert(stream.rtp_channel is not None) - assert(sink.in_use == 1) - assert(sink.stream is not None) - assert(sink.stream.state == AVDTP_STREAMING_STATE) + assert stream.state == AVDTP_STREAMING_STATE + assert stream.local_endpoint.in_use == 1 + assert stream.rtp_channel is not None + assert sink.in_use == 1 + assert sink.stream is not None + assert sink.stream.state == AVDTP_STREAMING_STATE stream.send_media_packet(source_packets[0]) stream.send_media_packet(source_packets[1]) stream.send_media_packet(source_packets[2]) await stream.close() - assert(stream.rtp_channel is None) - assert(len(rtp_packets) == 3) - assert(source.in_use == 0) - assert(source.stream.state == AVDTP_IDLE_STATE) - assert(sink.in_use == 0) - assert(sink.stream.state == AVDTP_IDLE_STATE) + assert stream.rtp_channel is None + assert len(rtp_packets) == 3 + assert source.in_use == 0 + assert source.stream.state == AVDTP_IDLE_STATE + assert sink.in_use == 0 + assert sink.stream.state == AVDTP_IDLE_STATE # ----------------------------------------------------------------------------- @@ -246,5 +280,5 @@ async def run_test_self(): # ----------------------------------------------------------------------------- if __name__ == '__main__': - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) asyncio.run(run_test_self()) diff --git a/tests/avdtp_test.py b/tests/avdtp_test.py index d14e4fa..1ca5254 100644 --- a/tests/avdtp_test.py +++ b/tests/avdtp_test.py @@ -28,7 +28,7 @@ from bumble.avdtp import ( Set_Configuration_Command, Set_Configuration_Response, ServiceCapabilities, - MediaCodecCapabilities + MediaCodecCapabilities, ) @@ -37,24 +37,28 @@ def test_messages(): capabilities = [ ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY), MediaCodecCapabilities( - media_type = AVDTP_AUDIO_MEDIA_TYPE, - media_codec_type = A2DP_SBC_CODEC_TYPE, - media_codec_information = bytes.fromhex('211502fa') + media_type=AVDTP_AUDIO_MEDIA_TYPE, + media_codec_type=A2DP_SBC_CODEC_TYPE, + media_codec_information=bytes.fromhex('211502fa'), ), - ServiceCapabilities(AVDTP_DELAY_REPORTING_SERVICE_CATEGORY) + ServiceCapabilities(AVDTP_DELAY_REPORTING_SERVICE_CATEGORY), ] message = Get_Capabilities_Response(capabilities) - parsed = Message.create(AVDTP_GET_CAPABILITIES, Message.RESPONSE_ACCEPT, message.payload) - assert(message.payload == parsed.payload) + parsed = Message.create( + AVDTP_GET_CAPABILITIES, Message.RESPONSE_ACCEPT, message.payload + ) + assert message.payload == parsed.payload message = Set_Configuration_Command(3, 4, capabilities) parsed = Message.create(AVDTP_SET_CONFIGURATION, Message.COMMAND, message.payload) - assert(message.payload == parsed.payload) + assert message.payload == parsed.payload # ----------------------------------------------------------------------------- def test_rtp(): - packet = bytes.fromhex('8060000103141c6a000000000a9cbd2adbfe75443333542210037eeeed5f76dfbbbb57ddb890eed5f76e2ad3958613d3d04a5f596fc2b54d613a6a95570b4b49c2d0955ac710ca6abb293bb4580d5896b106cd6a7c4b557d8bb73aac56b8e633aa161447caa86585ae4cbc9576cc9cbd2a54fe7443322064221000b44a5cd51929bc96328916b1694e1f3611d6b6928dbf554b01e96d23a6ad879834d99326a649b94ca6adbeab1311e372a3aa3468e9582d2d9c857da28e5b76a2d363089367432930a0160af22d48911bc46cea549cbd2a03fe754332206532210054cf1d3d9260d3bc9895566f124b22c4b3cb6bc66648cf9b21e1613a48b3592466e90cee3424cc6cc56d2f569b12145234c6bd73560c95ad9c584c9d6c26552cea9905da55b3eab182c40e2dae64b46c328ba64d9cbd2a3cde74433220643211001e8d1ad6210d5c26b296d40d298a29b073b46bb4542ceb1aea011612c6df64c731068d49b56bb48afb2456ea9b5903222bb63b8b1a60c52896325a22aad781486cdb36269d9dc6dd38d9acf5b0e9328e0b23542c9cbd2adffe744323206432200095731b2a62604accea58da8ee6aba6d6fc9169ab66a824527412a66ac6c5c41d12c85295673c3263848c88ae934f62619c46ed2adccaaeb3eac70c396bb28cb8cecaf22423c548cd4adca92d30d1370ba34a772d9cbd2a3efe6442221064322100cc932cd12222dcd854d6da8d09330d2708b392a3997ec8a2f30b9312b8c562d9353513eda7733c4b835176eeca695909cc10d08614574d36cac669c583e68d9778daca9b92d6e4bb5cd008ef3562aa52332bc54a9cbd2a1efe6443332064322100a6e91a6ddc58a3a4b966a3452cb6d0b9c5334d2b695929128dcd6123b8b366d491122fd545f9b96cf769d530d2e2646b15c6a43695b12d33aa214e622e45b1ac132309a39eddc82caad35115b3d2350c5c6dcd749cbd2a9c7e654332207433110086ed5b68531a54c6e7bb052d15add1b204bd62568d8922d3379418b9c4e202482909ab712a744d81f392fa94193d62293ac6dfa7278f79b451c70c3b4b2b64d70f0b3463323c46f598ecd70d35e5a743282307099cbd2ae9fe654332106432110082acdb4aca734b843b6699f491ad3a511aab6db2344eeed386d0aa34c49c4b0a4b2aa59ec98bba6419b06310d2f9626c42a7466728f0ca0f1db579b46c0a701264e59153535228dc6497492dac722596138bd74a9cbd2a0b7e655432107432110056a8d22a62d643b428e513b52ea4a66c7a41991719370c8d9664ce2bca685dd2690b1c368c5dce36d26b38d10e0c672343ca8c25c58d0d5c568de433b7561c61268aaf83260b4b868dca8ee6dc6ba573abcb5093') + packet = bytes.fromhex( + '8060000103141c6a000000000a9cbd2adbfe75443333542210037eeeed5f76dfbbbb57ddb890eed5f76e2ad3958613d3d04a5f596fc2b54d613a6a95570b4b49c2d0955ac710ca6abb293bb4580d5896b106cd6a7c4b557d8bb73aac56b8e633aa161447caa86585ae4cbc9576cc9cbd2a54fe7443322064221000b44a5cd51929bc96328916b1694e1f3611d6b6928dbf554b01e96d23a6ad879834d99326a649b94ca6adbeab1311e372a3aa3468e9582d2d9c857da28e5b76a2d363089367432930a0160af22d48911bc46cea549cbd2a03fe754332206532210054cf1d3d9260d3bc9895566f124b22c4b3cb6bc66648cf9b21e1613a48b3592466e90cee3424cc6cc56d2f569b12145234c6bd73560c95ad9c584c9d6c26552cea9905da55b3eab182c40e2dae64b46c328ba64d9cbd2a3cde74433220643211001e8d1ad6210d5c26b296d40d298a29b073b46bb4542ceb1aea011612c6df64c731068d49b56bb48afb2456ea9b5903222bb63b8b1a60c52896325a22aad781486cdb36269d9dc6dd38d9acf5b0e9328e0b23542c9cbd2adffe744323206432200095731b2a62604accea58da8ee6aba6d6fc9169ab66a824527412a66ac6c5c41d12c85295673c3263848c88ae934f62619c46ed2adccaaeb3eac70c396bb28cb8cecaf22423c548cd4adca92d30d1370ba34a772d9cbd2a3efe6442221064322100cc932cd12222dcd854d6da8d09330d2708b392a3997ec8a2f30b9312b8c562d9353513eda7733c4b835176eeca695909cc10d08614574d36cac669c583e68d9778daca9b92d6e4bb5cd008ef3562aa52332bc54a9cbd2a1efe6443332064322100a6e91a6ddc58a3a4b966a3452cb6d0b9c5334d2b695929128dcd6123b8b366d491122fd545f9b96cf769d530d2e2646b15c6a43695b12d33aa214e622e45b1ac132309a39eddc82caad35115b3d2350c5c6dcd749cbd2a9c7e654332207433110086ed5b68531a54c6e7bb052d15add1b204bd62568d8922d3379418b9c4e202482909ab712a744d81f392fa94193d62293ac6dfa7278f79b451c70c3b4b2b64d70f0b3463323c46f598ecd70d35e5a743282307099cbd2ae9fe654332106432110082acdb4aca734b843b6699f491ad3a511aab6db2344eeed386d0aa34c49c4b0a4b2aa59ec98bba6419b06310d2f9626c42a7466728f0ca0f1db579b46c0a701264e59153535228dc6497492dac722596138bd74a9cbd2a0b7e655432107432110056a8d22a62d643b428e513b52ea4a66c7a41991719370c8d9664ce2bca685dd2690b1c368c5dce36d26b38d10e0c672343ca8c25c58d0d5c568de433b7561c61268aaf83260b4b868dca8ee6dc6ba573abcb5093' + ) media_packet = MediaPacket.from_bytes(packet) print(media_packet) @@ -62,4 +66,4 @@ def test_rtp(): # ----------------------------------------------------------------------------- if __name__ == '__main__': test_messages() - test_rtp()
\ No newline at end of file + test_rtp() diff --git a/tests/core_test.py b/tests/core_test.py index f4bdd83..6c9d0c3 100644 --- a/tests/core_test.py +++ b/tests/core_test.py @@ -15,30 +15,58 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- -from bumble.core import AdvertisingData - +from bumble.core import AdvertisingData, UUID, get_dict_key_by_value # ----------------------------------------------------------------------------- def test_ad_data(): data = bytes([2, AdvertisingData.TX_POWER_LEVEL, 123]) ad = AdvertisingData.from_bytes(data) ad_bytes = bytes(ad) - assert(data == ad_bytes) - assert(ad.get(AdvertisingData.COMPLETE_LOCAL_NAME) is None) - assert(ad.get(AdvertisingData.TX_POWER_LEVEL) == bytes([123])) - assert(ad.get(AdvertisingData.COMPLETE_LOCAL_NAME, return_all=True) == []) - assert(ad.get(AdvertisingData.TX_POWER_LEVEL, return_all=True) == [bytes([123])]) + assert data == ad_bytes + assert ad.get(AdvertisingData.COMPLETE_LOCAL_NAME, raw=True) is None + assert ad.get(AdvertisingData.TX_POWER_LEVEL, raw=True) == bytes([123]) + assert ad.get_all(AdvertisingData.COMPLETE_LOCAL_NAME, raw=True) == [] + assert ad.get_all(AdvertisingData.TX_POWER_LEVEL, raw=True) == [bytes([123])] data2 = bytes([2, AdvertisingData.TX_POWER_LEVEL, 234]) ad.append(data2) ad_bytes = bytes(ad) - assert(ad_bytes == data + data2) - assert(ad.get(AdvertisingData.COMPLETE_LOCAL_NAME) is None) - assert(ad.get(AdvertisingData.TX_POWER_LEVEL) == bytes([123])) - assert(ad.get(AdvertisingData.COMPLETE_LOCAL_NAME, return_all=True) == []) - assert(ad.get(AdvertisingData.TX_POWER_LEVEL, return_all=True) == [bytes([123]), bytes([234])]) + assert ad_bytes == data + data2 + assert ad.get(AdvertisingData.COMPLETE_LOCAL_NAME, raw=True) is None + assert ad.get(AdvertisingData.TX_POWER_LEVEL, raw=True) == bytes([123]) + assert ad.get_all(AdvertisingData.COMPLETE_LOCAL_NAME, raw=True) == [] + assert ad.get_all(AdvertisingData.TX_POWER_LEVEL, raw=True) == [ + bytes([123]), + bytes([234]), + ] + + +# ----------------------------------------------------------------------------- +def test_get_dict_key_by_value(): + dictionary = {"A": 1, "B": 2} + assert get_dict_key_by_value(dictionary, 1) == "A" + assert get_dict_key_by_value(dictionary, 2) == "B" + assert get_dict_key_by_value(dictionary, 3) is None + + +# ----------------------------------------------------------------------------- +def test_uuid_to_hex_str() -> None: + assert UUID("b5ea").to_hex_str() == "B5EA" + assert UUID("df5ce654").to_hex_str() == "DF5CE654" + assert ( + UUID("df5ce654-e059-11ed-b5ea-0242ac120002").to_hex_str() + == "DF5CE654E05911EDB5EA0242AC120002" + ) + assert UUID("b5ea").to_hex_str('-') == "B5EA" + assert UUID("df5ce654").to_hex_str('-') == "DF5CE654" + assert ( + UUID("df5ce654-e059-11ed-b5ea-0242ac120002").to_hex_str('-') + == "DF5CE654-E059-11ED-B5EA-0242AC120002" + ) # ----------------------------------------------------------------------------- if __name__ == '__main__': - test_ad_data()
\ No newline at end of file + test_ad_data() + test_get_dict_key_by_value() + test_uuid_to_hex_str() diff --git a/tests/decoder_test.py b/tests/decoder_test.py new file mode 100644 index 0000000..3be2c63 --- /dev/null +++ b/tests/decoder_test.py @@ -0,0 +1,47 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import hashlib +import os +from bumble.decoder import G722Decoder + + +# ----------------------------------------------------------------------------- +def test_decode_file(): + decoder = G722Decoder() + output_bytes = bytearray() + + with open( + os.path.join(os.path.dirname(__file__), 'g722_sample.g722'), 'rb' + ) as file: + file_content = file.read() + frame_length = 80 + data_length = int(len(file_content) / frame_length) + + for i in range(0, data_length): + decoded_data = decoder.decode_frame( + file_content[i * frame_length : i * frame_length + frame_length] + ) + output_bytes.extend(decoded_data) + + result = hashlib.md5(output_bytes).hexdigest() + assert result == 'b58e0cdd012d12f5633fc796c3b0fbd4' + + +# ----------------------------------------------------------------------------- +if __name__ == '__main__': + test_decode_file() diff --git a/tests/device_test.py b/tests/device_test.py new file mode 100644 index 0000000..1bcd0d0 --- /dev/null +++ b/tests/device_test.py @@ -0,0 +1,267 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import asyncio +import logging +import os +from types import LambdaType +import pytest + +from bumble.core import BT_BR_EDR_TRANSPORT +from bumble.device import Connection, Device +from bumble.host import Host +from bumble.hci import ( + HCI_ACCEPT_CONNECTION_REQUEST_COMMAND, + HCI_COMMAND_STATUS_PENDING, + HCI_CREATE_CONNECTION_COMMAND, + HCI_SUCCESS, + Address, + HCI_Command_Complete_Event, + HCI_Command_Status_Event, + HCI_Connection_Complete_Event, + HCI_Connection_Request_Event, + HCI_Packet, +) +from bumble.gatt import ( + GATT_GENERIC_ACCESS_SERVICE, + GATT_CHARACTERISTIC_ATTRIBUTE_TYPE, + GATT_DEVICE_NAME_CHARACTERISTIC, + GATT_APPEARANCE_CHARACTERISTIC, +) + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- +logger = logging.getLogger(__name__) + + +# ----------------------------------------------------------------------------- +class Sink: + def __init__(self, flow): + self.flow = flow + next(self.flow) + + def on_packet(self, packet): + self.flow.send(packet) + + +# ----------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_device_connect_parallel(): + d0 = Device(host=Host(None, None)) + d1 = Device(host=Host(None, None)) + d2 = Device(host=Host(None, None)) + + # enable classic + d0.classic_enabled = True + d1.classic_enabled = True + d2.classic_enabled = True + + # set public addresses + d0.public_address = Address( + 'F0:F1:F2:F3:F4:F5', address_type=Address.PUBLIC_DEVICE_ADDRESS + ) + d1.public_address = Address( + 'F5:F4:F3:F2:F1:F0', address_type=Address.PUBLIC_DEVICE_ADDRESS + ) + d2.public_address = Address( + 'F5:F4:F3:F3:F4:F5', address_type=Address.PUBLIC_DEVICE_ADDRESS + ) + + def d0_flow(): + packet = HCI_Packet.from_bytes((yield)) + assert packet.name == 'HCI_CREATE_CONNECTION_COMMAND' + assert packet.bd_addr == d1.public_address + + d0.host.on_hci_packet( + HCI_Command_Status_Event( + status=HCI_COMMAND_STATUS_PENDING, + num_hci_command_packets=1, + command_opcode=HCI_CREATE_CONNECTION_COMMAND, + ) + ) + + d1.host.on_hci_packet( + HCI_Connection_Request_Event( + bd_addr=d0.public_address, + class_of_device=0, + link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE, + ) + ) + + packet = HCI_Packet.from_bytes((yield)) + assert packet.name == 'HCI_CREATE_CONNECTION_COMMAND' + assert packet.bd_addr == d2.public_address + + d0.host.on_hci_packet( + HCI_Command_Status_Event( + status=HCI_COMMAND_STATUS_PENDING, + num_hci_command_packets=1, + command_opcode=HCI_CREATE_CONNECTION_COMMAND, + ) + ) + + d2.host.on_hci_packet( + HCI_Connection_Request_Event( + bd_addr=d0.public_address, + class_of_device=0, + link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE, + ) + ) + + assert (yield) == None + + def d1_flow(): + packet = HCI_Packet.from_bytes((yield)) + assert packet.name == 'HCI_ACCEPT_CONNECTION_REQUEST_COMMAND' + + d1.host.on_hci_packet( + HCI_Command_Complete_Event( + num_hci_command_packets=1, + command_opcode=HCI_ACCEPT_CONNECTION_REQUEST_COMMAND, + return_parameters=b"\x00", + ) + ) + + d1.host.on_hci_packet( + HCI_Connection_Complete_Event( + status=HCI_SUCCESS, + connection_handle=0x100, + bd_addr=d0.public_address, + link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE, + encryption_enabled=True, + ) + ) + + d0.host.on_hci_packet( + HCI_Connection_Complete_Event( + status=HCI_SUCCESS, + connection_handle=0x100, + bd_addr=d1.public_address, + link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE, + encryption_enabled=True, + ) + ) + + assert (yield) == None + + def d2_flow(): + packet = HCI_Packet.from_bytes((yield)) + assert packet.name == 'HCI_ACCEPT_CONNECTION_REQUEST_COMMAND' + + d2.host.on_hci_packet( + HCI_Command_Complete_Event( + num_hci_command_packets=1, + command_opcode=HCI_ACCEPT_CONNECTION_REQUEST_COMMAND, + return_parameters=b"\x00", + ) + ) + + d2.host.on_hci_packet( + HCI_Connection_Complete_Event( + status=HCI_SUCCESS, + connection_handle=0x101, + bd_addr=d0.public_address, + link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE, + encryption_enabled=True, + ) + ) + + d0.host.on_hci_packet( + HCI_Connection_Complete_Event( + status=HCI_SUCCESS, + connection_handle=0x101, + bd_addr=d2.public_address, + link_type=HCI_Connection_Complete_Event.ACL_LINK_TYPE, + encryption_enabled=True, + ) + ) + + assert (yield) == None + + d0.host.set_packet_sink(Sink(d0_flow())) + d1.host.set_packet_sink(Sink(d1_flow())) + d2.host.set_packet_sink(Sink(d2_flow())) + + [c01, c02, a10, a20] = await asyncio.gather( + *[ + asyncio.create_task( + d0.connect(d1.public_address, transport=BT_BR_EDR_TRANSPORT) + ), + asyncio.create_task( + d0.connect(d2.public_address, transport=BT_BR_EDR_TRANSPORT) + ), + asyncio.create_task(d1.accept(peer_address=d0.public_address)), + asyncio.create_task(d2.accept()), + ] + ) + + assert type(c01) == Connection + assert type(c02) == Connection + assert type(a10) == Connection + assert type(a20) == Connection + + assert c01.handle == a10.handle and c01.handle == 0x100 + assert c02.handle == a20.handle and c02.handle == 0x101 + + +# ----------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_flush(): + d0 = Device(host=Host(None, None)) + task = d0.abort_on('flush', asyncio.sleep(10000)) + await d0.host.flush() + try: + await task + assert False + except asyncio.CancelledError: + pass + + +# ----------------------------------------------------------------------------- +def test_gatt_services_with_gas(): + device = Device(host=Host(None, None)) + + # there should be one service and two chars, therefore 5 attributes + assert len(device.gatt_server.attributes) == 5 + assert device.gatt_server.attributes[0].uuid == GATT_GENERIC_ACCESS_SERVICE + assert device.gatt_server.attributes[1].type == GATT_CHARACTERISTIC_ATTRIBUTE_TYPE + assert device.gatt_server.attributes[2].uuid == GATT_DEVICE_NAME_CHARACTERISTIC + assert device.gatt_server.attributes[3].type == GATT_CHARACTERISTIC_ATTRIBUTE_TYPE + assert device.gatt_server.attributes[4].uuid == GATT_APPEARANCE_CHARACTERISTIC + + +# ----------------------------------------------------------------------------- +def test_gatt_services_without_gas(): + device = Device(host=Host(None, None), generic_access_service=False) + + # there should be no services + assert len(device.gatt_server.attributes) == 0 + + +# ----------------------------------------------------------------------------- +async def run_test_device(): + await test_device_connect_parallel() + await test_flush() + await test_gatt_services_with_gas() + await test_gatt_services_without_gas() + + +# ----------------------------------------------------------------------------- +if __name__ == '__main__': + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + asyncio.run(run_test_device()) diff --git a/tests/g722_sample.g722 b/tests/g722_sample.g722 new file mode 100644 index 0000000..432cd6c --- /dev/null +++ b/tests/g722_sample.g722 @@ -0,0 +1 @@ +Œ „ „ , /š1"9)§1 ²,¢/¾¢½ÍY_ý~|ûýÿýÿÿú~ÚxøØ|øÿÚYùÿ^ÛVZÓX|’¿ÏÕ_—WÞxû•Ñ:»TpzÑÛ³{þ}øüù^~îõµ²ßùùz¶ío}»nµ}ôØnñ?÷ýï÷ùx]øo³Zûþ_ü:–08Λµ\þ´îÎÚuÚpïNß½VÕzòYžú¶\ÖÖ¿ÿwœüØøнÞ×Ûó]ÓqÝSûŽ{ß:Y²þÊ\zY¿Þ—x^~ÿÞØ~ŽÙøÖûú˜í~só´.¸j—wñ]xyíêu—oôøðîxq–z¼0{ÿôœÿ6ÝînÔÕÚyÛö:Ò{XÚ^YÝkTŽxœ^~VÑ_Ú™Þ×Þ®ÙOý0Þ|ûÕû[›0«ù\Ù2½sœñ³W[Þv’uèu¸xp;ò™»ýlÿ¼ð¿ú|ÚñpÜ»ZÚ>>XÕŽ_ØÝ×õ³YÏUØUõìõRÕ>Þ–œpùRx”ø÷Z¾ú_tu2ù.Yò˜™m¯÷ýr~˜^_èö^u÷÷ù¹Ôù¿PÝqñoxÞ´×=þmì¹zÎx¶Ó}Þµš’Ü÷Z}¸ØZùók»üÑØïÖØ8öZøß÷šýiv–ðvúuÛu—Ð_×s–Xú~Û“²ÖUŸûÚyÜÎvÒØÞwù™›¶{sî¹ö°_x²÷ð»ÙrÝpxÿ{tîy°ý?Yj¿YÐÞ³œ×ÔXóÞýtºLÕ^Ôs–µÚþßs[.úöúün5×ÿÖ¾ÓÑÙÕ
zÕQžßšTÚÑóõø—|óžsüyqõšôm–ò³u½sóô´t^ðúÕðëwÞ´°ö¿ñþ÷;ÖÿsÖßÛ[¸˜ÐzùX¹—{Þ´RÝm›Wî™ÑìÚ_zÙ\rþUœtõø——p¿^s\µY˜]š~~sóu½ØÍ{/ÏúYÿÜoØŸ¸ÔwÛ_šøÓUôúõÿÙötð¹zõq|¿æ´vïò›þûôóü²^8Ùóôÿu°5õÞüÝÞ;ÒÞÜUZû›Ú›þ]ÙØþ~TóX5úœ•ÛþÜ[k–œt{Õ{ë™íòvßúžk¾š,Ü›oÕ×tÚúÛp]Ï<œU‘øîÚ{]Õ<Ú6}w»^ù_Z÷÷ñqÿÑ?oµ[qøP^±—]óîÿÍ“9Õ]ùÜÜÝXßîŸÿí½~óÚò®¿rò4|ý™_¶pquóýnž_óû«òxí{¾öø:¸ó1™ñ^Q]”^—~QÛòü[UÓ|›_º×þ]\™ü×UÝÍÛxÒW;×VÐþþ_íÜ–¸ÿv÷ôv;]±1o4zÝqöórþë\+Wsòºøt/uÙ·êW²sûÚöyü]5ÿ[¶Ø×z¾WšŸ±Ø~T×pôÛ²Ó_<wÖ]ûþUÔ|—>Ü^sŒ™Î}WþU‘šÖöØÓý}ÑY˜xõýñ[tñßz²ßº_¹oŸoùØî]ö®öõ4îv_vîïv¹þ|ô}µ+Þs½û“üî?ÛîœÝ{zwVzÞê—Óú}ò•¿QÐ{—úžÛtØÖ×ú–}sr}ôuT›xu[Ú»[{\™ýñÛS±=›s÷9wøÞ·¼èßÝhtýøòwúï2ÞW¹rõ‘ïyºßtÕqtÓsº‘Ü\Þú\ÔyMPÝŸúÓú‘ÓœöúþÖ[Ñ_´–2|[Ÿÿ][¿süø_رÜ9îßß]öõo[ž¯ùþqì°õô^ð¸ö¼.ð<soÙroØùr¼Þrü3V-Ö|–>|ùyõòÔÑyÝx5œzYÝÛwÙò˵»[Øv½ÏžÖ¹sTzÙÜÜ4roÛwõ6¯õtyïßsž¯°ðýßoÞò}ÿôÕ•Ü>Þy—SõÎw¼÷Wÿ/Ð]¶í—[þzYóÖùÚ?ÖÙÚTÏVÙùøZÖs•›¼YÚwx“ûò\Ï~_šž³“ÛÚ}þܘûÚ{Xÿg”\÷_ùxñÝðµ»ûöqÕò/]¹º[ín~ð³´ý~xªñ]ùrþômù¹Ÿ÷\j¶ö´Úp»ômzl×}ôûõ÷¹ZÔøöWõ9œ’]^÷žÕõØÐØ{ÖÞÒÜžÖÙ{’Q¿Úûtùל}vqûwšYøôýûðŒ6/Õù³qß¼ïØ[¿ÿ.Ô}ü[PÒ²Û_ûÕ_˜8Ï3ÛÚÛ¼÷~1_[”÷õýøüòÝ[v´oxžÿnÝ?ÛóüÒNüþÎôZÐœ“TÖ—Ô˜Þ×Sww½wŸ}”^tywú½|XxiŸ?èløüå²÷øsÿuóú¹<núÞúñz¾qµ^nŸ÷}´îôtîêšy¯ðœýpzÚõ«ÛÞñ´}[2¹?ÓoûÖyÐWÖ}Ýß•~WxU—XÖÚXŸŸýß]Ï|µ×_ùî_ôX—î[ØŸöVt^™¾ßÎ8~×÷öø‘;Ö__V·ÜVö2ÙtœÞúSïÚþ¼ìÝßtrøÑùôœù,lœ-üWðtìösöÙór_¶´ü¾x´<÷î¾ôüÚ÷=÷/ýÚ±²ÿöSrÝzxl®ÕÙ}ùÌú»RöëÙø´QšßÑ_ö–w›uÎÙüTØüýÜÞÔT”VüÙVqœ´ºÙÞßW}h\ÔkjÒ¾òñØ×mr¾ß,´rØùŸ¿ù[ò¬t8ÝrÖWzwÜzó·ýØð®X¼ªÝµlõÙuwÿßú»ôþÖwŠQ^Ó™Ý]›Ýw×–ü¹ÓöÔ^õ5ÿYöÖÖr³~øi¸ÚxšÜþS÷Üõ_X±ë˜|›¿ÔÖqÿ}ö÷“ÿÙYrQú+«;ß{ùQÐÚûÔºªõØÞ8šÚUÖZös²ôÜY^ü6ß^®»¬ñkvøùõÛßrë.²ìßíñœÜ¼ZtZx¯÷Ý,ô~Û°Õ_w~mðš>_ÿÓÕ8ù’ðŸwÕœ\RÎÝVWôØU³Z¸™×ZÓÖü]žßu›]ÑY\œÐž¸ïYÛòØÏZVu×¾?±\s9{Õϵ]œôüoÞwÜ~¼tû|zjnz¯ýÝž2ò¹qìoùöoÑWô_õ_òñúöžžñ»Õûø(w’øüüúÙ_sÏn±zÚ¿·XÓ}øÞ–ÿݺy]óšÖÕmÚ¼ÙÔÙÿÖÙ¾ßÜ
oyÕòš›ÞsÐ|r”ºt•RÚ_ûÙûy÷Û±Üux¾Ÿ“_ySìz²›ÛöÒuiÕ_zš|Ú–6Úu¿]ÙîéyÚr}ÿžšj¶YüµWþ]öÛyßÓ}]Û.Õ6ïU¼´ªöWp'ýwíðôïpîø´µú{ùôšuôüõûZ×Ø}ÞX»_”ÒŸž[P[ìØ
xµ^R\×ýØ—<»ßõ[Ñ~XUÿ½ÚþÝ´•ûýQОœðüØÛ÷6NÖpw‘õößYÜo•÷[t7¯úqóÜ{•r¾þðpnÚü^غoÔz±T^rU²¹û-jõwÛuùµú÷ôó]ù¶oXý¯YŽõ>™ÿ\pq\ÞŸÕ|ÜÒ2ßxð½ÝùÿØõ~o{¿šë}Üzüݳ¾k±öõïýß{|öžò§Õþkîöš2\Zÿ•rºóþÕÝÖ¶øVùû•{vs–vþ“ÔݾY“_X×îV”WWžr’SÛS\U×™Ó×Ò°]ÿöü\žÞÔSmwš7ö–_¿ú×T÷~×}ùöÞU~–}rrz»stÚuÙ?Ýp¿¸lpvò±òß|æ=nòx2r¼k|ôpúð3ûn¶÷^¶ry|«Üÿsõ:Ôñzš7ÒuöÍ]6ÒxšùV¿Û—ýUy^ÔÐûXØ››ôÒüXýwS–ÜÝßÎÙ±ÜÑóz”ÿ|6_¾WžóžTøíW9ÛøÞݸÿiöòÿòuü´-·óÝsõ¸¯úUïüwXð°>ßvôZïýÛ_¬üØù³ÜXðØ’ó™Þý_îT–/—ÜzýÚÒzóœØsÔTܼ—šÖYRüÝ–ûØ^Ôûšû–_œÞ™öÕÐîû\þUó.×÷üòò]³.Yyò›Ýí[˜¬-¼-ÓkÚxóÛ1þøq}xìw¶wÓÞýpr¾ÛöYÛîœß?š´÷[¼ßß7}™T{—ZÞû:×õoÚTRšÓY}]ÙÖüWÔx›–¿Ó.Üþ^üÚ{¸Oltö>|øð{îñy²Ö¿Ûv±þxÚl§õøÞW—wo¹×ñóXû÷ý·lóÛºw½ßðÜÔY—þöûÓß}þ˜³öuxQŸs6ºmžûüXtù}ó×}SÝT›YÑ8}ø˜S¿RRXUV—¾zP\>ÚÕxžZ|¸ÏTÞí–×ñ}R[²xZZùX]î0똱³ùæô5ßyyr?³xîßø7·9[ñzžÞªn²nú\^sû}²õwÞû¶6ÜõØY_õœWZ³]^½8yÓwüý¹ÜQÒsVÝÙ’Ÿ×q–ÿÚûÝ[ŽúÒî=÷TÔSÔ˜u±ýó{\U׸œÐœøYño¿YYÿ^ýô½Xs|]”ÛTœTÚøíÛyó|\öóqüð]¿r/]ðø3¬qlõÝ·uw«_ï:xûît›]µû•üùžÿÜÓüÕ»ÙùþØ\{¾þýôÖ²r»
üzÿðÚ×ßwZ~[—ô÷RØÓ1TÛtXRÖ¾[Þ•±×Ù7Sô¾õmuYúõžïu÷ußv´÷vôõª4Ýóú3ÜvµuùÞÖ¶÷ï–_rܼòp½qóÝÖøjýßr÷Ó½þš÷®ÕZÝWÿxXX˜™]úZ‘õÝJ^Ý}úS¿û[•÷•žžÝù~ÞT|•X°¹¾Ó÷xtÝëZí‘ñîX«\ôwqü_±3^ÙñíxØZí–]íZ¼[•xžÞušÚò[Tû+Ú]U}Xñ÷•ÖZy¹ŸuÞÝi\ÚWñ~Zªð¯*ºX÷~ñ\þÜ^ùÖü7:]X˜ËÐÊXØv·}´]óÞöúýº¾ú.ò^½º÷ïZÖÌLÎø_q¯m:qpï½±´öms¹úÝÙxþò2õöÌ×_Ûúxöw¹x·ûvøn¶výöVØØVu›øîK‘ÕÙÚtž:Þû³_õøqøð™]Ù˜SýôòµJØÑü×Ö=÷¿õõvóþîôì²ß:ô:Ópý˜zñôsNšÞ]yV¹¸òûpxöóïwv¼[vø•˜^÷òº|Ҙ˜ž]›ù×tûym½öºóõß<Ý}pÏõÓðn¹XÛÏVßœWþ×7›y9ÿø+ùëßz·Õý´úÐWñsXÎSùØøÓœï~û›ñw6îü÷ûvÞ»]Ø5›Ÿÿjz¸ÌÉœ}—x¾U÷zõ_ñÿ÷õô¹9öœžÜúØü[ìëSÙMS<ùÝÕ›,y:}ú¾éýí0ÕzµwR»3Ñí9øü\ÎÖÖõ_‘|¶ÝußüóuµóßòVuy›Ø“·ÚøVlxÚÍP“ÛýÝÛoÝý³þôì¶<ñötº¹Zü¼{upx6N–Ï{ûZû•qö4ñ}ëûr¶xõÛrÖ7÷Tžk\V×ÑœþÒ{úøÞX¶ìwúèØY.º|SÞ2š}¼ÞòÖ^OÙ™Ú¾´Õ_ö-ÜÚ¬ìuq®ÖttÜû\0·Wuøy\“T’zØUÝvýÖ+Õúòv¼ïtÞ¾¹›ý×^ßzÝî³ÐS[ÖÜŠzužøùû{ú¯–-óZö>ôR[õ\žºóúR;OUÞÌZ½>|ízùôr^õ§ú•óô\[¶_]¬ìVYßXÚPV¼[¹s{ü°rtº²ñöù/Ýö^úýV3ù’[–ÑÚU~ÒXþrùu°ø°÷ïøÛ²úÖ{YüVïïØ×}Õ–Û{˜vóW¯øû}ò<ê\ùžÛðÑ™öÖ4ôXZ½ÑP}–ZÛxvÞírÿ÷·ðñ_µutÚ½z2Ñì»ÐzÒ[˜ZÚÛë|õyòq¹~ôµÛì5ÓW»<ÖØò__7×M_—Þö›mþ<q¾°ñ½wð~üyÐô˜‘uûZìšR0’ÐXSÞ›¹è{ÜovvùyõrºÚtùTS°¿_Wµr—ØV=ÑÑÞžÚroúôrurôþ³õô\T{Þyž|vž™’žÍÕx’}÷vò{û÷ynYöq²[œ~Îô³Qû>Ò{žÙTÏüÔÛ¸;wšñìóÕsõ|Ÿqò”ظvÑ]tu™ÖÝÕØM³~Òðñ|r¾î·ûšì²ZðÚXø—8ûÒôTP[Õ×õ“sþróv^±6ø:¼³Z¾›üÝþ~w7]ÕÑZÕÚSWs°þõìúúïÜq¾þn¿Y™¾öמvS|tXùLÐÖþVù•¹ìø¼ìm½µsxô{þtW½êPÑø²|ßQšÕZÔ›Ôšùö]¼vòׯòùužÒpðѸô“YöœÞ_“Ü^ÕÓÿ›vŸøn}¸éuøvõ|™û1üz×Ôë^’sÙXSZz’{¸Þþ0víœïøù1~Þžüq×ö–ÞþyÝÍq’’ßÛw•<°ßî|{ry÷ùïW´šß\w{–Tü«SÛØRú_žvônÙp»qiÙ_êüº™øvSùùÖ6½}VX”yÑÒšvó•µèÝ|ªÙu¶wÝrW÷º_Ó÷Ú\zÜÖ›¿ØÓ>›ïÞÜîòÙóqö´ïTžö_rÑ:”Ûv^ú‘|ÔÍøþt}´õ0óØõí¸uw]üüý–øûÕ8›÷yÖRYÔνžw¸õoóòtýöòñº÷ùz“rþÖ6u—9›UQÚÖ]“(îVßnìÿÚ/»ûZ}íŸ_Ü_WîÞÛ=ÕÒ˜¿þŸÑûpyxfÖ|îõøÞ]ÚxßWúS¾z¿]œN×z•–öþ[võíºôñ|ßnuýœµUQø¶ÜTöÜ™ûÖÒ~•RX}¶÷5ööôm³µys÷ßò;\ܾÓVµsþ—°ÖÚu{\uîùo¼lüríþ:üÜõZÝ›×ûV~T^—ÜSü}š\V½l·´”uotÖ{ïüÝWñ™›öÓxÝÑ~öÚ»ÓߟúÕ{ëVn-Y¹îùp{xêW¸2žt\—øyÿxø“mXÑxÑ]9ø÷q–ø—Û-ZuÝ8›^ÞÚx²nŽ˜vW¾|ö¾ÎÞ6TÕ÷r½o_¼=›²ÝmËùZÛõß]øv“zkÔ¹¾ß÷Ú¶4Øî¯WômzYÛ}™ñ\ùØ×]~uïÐX6¯ë›üùtw&ݱôq°<9p÷×pó:Ùn~ÚùtÜY9]s›’Ùú|öU—þÛ:”Þ—Ù×\ú^ËxTÛ›˜Y{ÐzÖ}VÝÛ)Ý2–õšßl¯~š{Nôݼwš1_Óp|Ûòñ˜lô5ùVôë|õpß3ø*v¶=r×®ßýõp—m»ûùÕZRöøUßÓöŸ8ZÛ¹z[Þ>~V}yÞ/œÚØöÓþÜSV߳Й7ÑÞ\Õú·žþÝ}Õ÷_Y¼\ûxôwx^.Ò«üvÚ_±j18õ]wÚòz.sòqñZl+ñò5ó[ðñù2·uÛq¾Û¸üíÙ›ÞýÔùkÍÚ¶<œÞÝtôÙØ=PZ~Ù?’ÝÐ_PÛz“ÛßXZ¿XsÒØú~Ô}ú’º^zÙ8Ú1ÿrzÜô2ôôôz-ñuÖö«÷9o6šyµzþnR[u5|uÑú]Oö]ÿU_gþø·éô|kX]Z{¿\¼k›qvÕP‘SPTžz¸lmp2x1ýÑv^–úxmv|i–Ð×N‘RS^ütñ2ìsü1¿^»Õù.×jõx±Ù^MÏ¿ÍzwÞ.íóusþúÖÝø»þkÚúÐÓQL—ØÙwúo´nwµù¿Sw^“ú÷{wñ¼»\UÏTTÖ=²úïmòvòÛ<ýZ•õo]r¯ûûÞÏ׎VœZ®wøm®~õ?_[xœÓ°r¹uïÝ?^Ô‹þÎ[Ütñ¸+õùº´[ßòMòð|q¯Õ2ÿÓ\Ú˜šŸ´óxmôõß3×\yÜÑê8ßloOîXÒW˜ÔýW|¯ý¹1y^½V›|×·vóZð1ZÒÜÒTTÓþù³þµõ+]×vÿV»º.kß:r¼Ú\QžÞ]ÛµðìÿöyzØÜZqÿ•fsm]ßÒÓ[•þÞm°õyóž~YòPíX0&prØØ^Óߘý|±pºýqýýWØû™>¹}´¤™ÝrÑWþÍÒÙÛôØŸnú{Þ:×¹¾šÔï=~güZmvNýX‘ZþÛ8¶p´rùù¹^ÜtûØ«\ìfÝ¿o\“uÔÕù¹ôðövùøÛ´{’kÞœ(hÔ}tÔÒ~M–¹ÞÿöößñÔUšyY”øÝøgÝ}ÜR[ŒÒ—^ù93¹ó¼ø“ù:Ò-ù~èkQòxÓÝNÜ}y{³pô÷ûÿZÜó9“)=ùe÷uy•ÚÚÔQúõÞðíörú<ÿÙø|Þ1r—è(úRיГð~Xvó~wØ–WÜÙ’t¸ßëú–û¿ÏSQR›ù^ºíù:sœÝWwš:éÙxc[|¶ûÖ>ÙXÙwšðl¹vx»Üüûx›®k›}0YÐZÖÒÙ¾}<¯ïÿ7u~ÛÜ~:Ü/uÖå/×vþÔUØÑÙùúZ¯û¹óßÙ\Y»<Ú6u–é.ÐúûÑ×ÛÐPؾ{Úí¶wµ¶ÞÜ>ü4Ùn³Õ§ôýÜsRÚÙÑ×Ø~ÿ›l±ßuþ–~›2šo}~è-Wò\\•QÑØ;{ýôszñ]ÙÙ·Þîv¼d2“kZÖ\Ò—œù{Üoß3u_Ü]8|~ø^½äøЯW‘ÞÛLQÞ¼výò÷qrŸ¹šzºñØpv<¤QïØÏx×Õ•]ºžvñy<õ||þ~¼tò¯û§^Ø3WÒø˜ÎÖšùÝyû>ružÝ™zðvÙì6ÙföXt4ÔŸ’œÔxüõíùtqtzØ~·qœpøÖ¤/{u”ÛšÍZ\YûZs¸õ>ÞûYØü0Û|1èïM|ñRÙ”PV^Ú×ôt¹ûwßsœ·÷ŸoórhœÝïÌ{ÒSXÛw¿v÷5n¿uÛ|³6Þ7p×ëñ˜ýïÑÛÎß”Vÿ<qø´õµt\ׯ|ú{¯Ôsj–÷vÓþØÍšÖ|ý¹´óûÿö}Ù¸õ›·ïÛZé}üm”6БÓÕÞwX÷2óvw\¾{Ÿv½n=ÜêsØñÿS\RÛSšßÿôÝû1Ýþ}›õ¼ó¶’p.ØuúQŸWOTü<v²õömv^úþó\w²Ys©Üo{“ßÝU‘ÐZ5ú~ôprp¼Üúy2^ܳ2¬ÙìÖZSÍY’ÔžÞøps:ð8ywÕu8÷rÛçW6ûÖXÜÐÕ×ÔÜÞµ6¸q62Ü<v˜mÙ–lq×qßþÚ‘ÑÖ~Ÿï÷ÿ-o;r×vq|üz^ovsw˜]SÕ¿SÓ|ÜøïÜ0¶q²Ý×pô»Õ(]Ú¸zûSRÚÓÏ>Ú›zýð/ýxò^t{WpŸÒw¾>ÞûÿLÍþÒUÏÝ=ôZ·ö´2ûzý»3Óxtô{ýúûÕXüØþÙyñŸjô&Ùs¶¶þ´xÖùwúýßú›W›ÍW·R_önó4uðúžöžÚyúS™ðûVTû}ÊÜ›\Öñ\òþð7ó5óÝùsvØ?Ÿr˜qyпºÒYþUx~}µö¶ýêör¹î¿vøÐýuÙøô˜ØÓ”Ïñú±Wýôìÿ7øt”÷oÛ™Øú^_ôW˜__ÜKÛ<óзÛõ7y5ÿpz_³ÜÞßõ~X8|™VÿZ–ÓWßüÜ6žûn-øôë»÷0ï}Ööö³|œPsù”ÓþU~”Üöxþl°üzŸé÷\ÜwºÙý^ý¿{ËXöÒÞÙú¾\v~nšî±|ÿ]k^½õ»ûßî‘Ûy_“ïYTññz]¸ôñqôoÜúðrÔß¿ÓïzÕ]½¿ÔÓ\9š›rôûý0wx¬üò•þy{ÑÖ6ÜþÛÎÝÜÓØÕøØö¯\ºðõö,øv¹]þµ3L{pöØÓ¸ÔW:ÔRò÷[¶÷ô7¸îøïùÕuöZœ=´úÕU~Ó]Ó×Û=ÚWntšðöZf?w¸wXôX;”»ú¼ÕØýUÓúÖ<¶ûZµ¯Vóõòõpû~ðýÒòßÿú\’¾Ó•ÔVzþù*4±Þðusö^ûøœ[t]üwV¹PTÕßsünYw°q·ÙvªZ\Ùö—ñPYµ\»úÐR{ŒÓÞ›uù²üs±öx¶µúwþ{=ûýþøÖt½ÚÛ‘ÔÒX×m±³ß®rÝ~°mö]»ûòÙZU¸mš]ýÔþTSÞÙÞXº1¼zèqŸsõ÷ØsúVõw‘UðœwÏûÖ–žU×½¿Ÿôî~_w*r’8úù]Ûò¹’ÖyÖÞQ^x–ÐS—ÞŸôtšò|nvôøòu¸Z-˜Ît¼nÜÿYÎûž™Ý\ÔùÜ·i¹¼îuír\ø3ßýùušÕ4tÓ>[ÞÎÜ—>TþöÝu¸kýÿì¯|ÿ:·Þœ±û\TvýÕÖ×^˜VÛvÒžix¸qÝu¸®úõ|Ù~ÕÖíÖO±TÓÝ”üÛ{ïüq”ò÷]³°ß[Ÿp¸YÝù~ò\Ú[ï
X–»Ñßfìžrõš4/n[xÛþ{uKmØÖ.ßžßÓÜ\Ïóû—pµ¿q÷þ¾øsšsÚ½üÙÒw[ïòWXšÒW›û~_un¶ó°úùù}4ô6¾Zù|O{·}n½šÏXýNVqtîµôwvýÿt·ºòºØ–4•ÑxoÜð_ÍÓ\ÖYWx»÷ïºv›òýûWm¹{¹\–{Ëõs›6î‘–þÑÒÞYùt¶2úvõûœœúnôtÝ[ú×ôñZn»O½{ÌVý¿óïµ<ùðúÖõùù´ñY›zÌT1öÛnÚPØžRÏøïšµk¸xµó[¸òõ{8{ßö”Ö{ôöýsSÒÚÿÐry»s´u]²0^×ëvþÿž_ÜSž<´XúÜÓÓÕÚ·Ýù.±üYrr™vîþ;ÿûÚ˜O3Úð^yYŒQÛQÓ?}ò_õ°Ÿ_urœ¹ì®~^Þ6ÑU2òºô›ÜMÑZW”ßoóõóò´}ñ,ôzky·V¸yU\rñ[y[žUÖ\Ï0wr½þyðÞzö6¹m0›Ößv\÷/ß™ÖSQWWÚxòxºXpuÞ3¼õ²r²ÖZú}‘ÚnvuQÔšR“]Ô\>÷ñùÜŸqµó_ô¯öwÝÔ9¸Î÷,mTÜ×VÕÖÙüqŸs÷Ú¹ÿòøû0/mÑ]u¿Íz¨¬Ø”ÕxÔSÜY¶üvünÝŸ0qõ:}¬{WxUy]+³œÑUÛWÒ~]õöÞršüy{î7Xðëþ¹ýŸSmjžÑÔÙ~ÑT^½}òxó×|nðøÿorŸwÖ¼7—]ûgú”P›_×Öœ\ôùzò»ÿ|±±ôõùsqÛÛ>Ý÷qnðùˆÒ9ý”V|-´~˜ºú=ZvïöÝr¹}Xؙߙ¿líÙÏYÒŸÔÿøö÷üßûõõöévZ¬tV›õÙÚ¼liÖ×U‘¸×™Õ¹6wÛo™÷Ý_vwô7þw˜Ü_û’ïsïÖÐÕ½ÐÑ\Ûz½q¾×òŸ:ðîüpóÚxÚûܳui·ÌØ\”R¿Þnžù»_ûz\éïWòø~\—˜Öés½ÚSVßTŽüÙ9jÜôõzùñº/¶´îØþ?}Ü\]åoÏ~–Ñ¿™þôöÞý9~[y²ôùûî›\úÕTÞ¾©i_νZšVÚú0oYóUøpÕÿ¬3Û+ŸÞ÷˜Óüßs.¹Ú×ÕÔØUv/úøõz»wÙ{¯õïòu¼Ö¾ùÚ»jvÿÒ_ÜTY±ò~þzü²U[î^øj™=ùZÛ=ÍÜ-ðÓØNŸ=MO_ôô[xržtÝìÝ|-±ÝŸvÞþuê}ZÐؘ“ÓÕx´x±öúùë“mº9óps•yý<]’pÞßSÞÑV[\{ñ¶ÞµuºÜùúpó1yZò9ÕõÝeü\ÔÖXÔZrî´÷¿ò^ýÞõ6¶í;ŸµÿRÝzómÖÔÛÒÏ×—u¼ôöÝúÿw™Þþn¬Xøðü–6óóÝÝ×ÐÌþÝuï>sžï6UÝnú¹pz[¶ûÕÑwìôÙ»ÑÜ’ÒØy²ñrxk¾ôÿÜ°î±»wûÝWýønš¼Ó:UÏVÖ´yùvu·ñXXpüõïÛóõ“øzþ·”Ð?RÓUÑþŸöøuvñYþú¹8õkžÝñøWÝZôsxœúКÐÎòzµl3ßð{žz²/~ôúý¹œÐvðïUØþXÐQØ÷¯Vq°óR0üWn¹»øõ{Ý^™˜z.Z™[ûRÔOÝ’.]|mr›w”ÿú]5ØpoSõÕ–ú÷ÔžÐÏ–vµððøöûTú»ÿötpõÿÿø]Ðó0¹Ó¿ÜÖQÑ^Ûó5ò2µõz¿:÷Vòëò–vo¾u¸úÞ_ÚûTŒZw”óñs¹-õÜôþ]qöúòö—]4ÔÜï‘yY’ÔR›õÚq³òÛÛpüØÕ-õ__°ø˜Ñ½tvÎXßþ‹Ú“ÞXÛôpó{¼´p[½4ñt3ýWºöü[Ûÿ[ŽØÓRÙš_íò^ðíµ^Üõ·šqÜrtÔX²ý»ÑÝZ“šÕÔÕØtôó·thößo›t{õ¸·6¾\°õþ]~™_ÐÏðTÎ÷»oÜðìþy«ÖÜ:õ~¶ù÷œ•uòÞsTžVÌYn“q¯|÷ýwÝÞ~Ÿzÿ8êPП«P\øÛ]VYœ}Ú«xµ]ñtÚXó\ý2–vy7^õuÒÕVþŒ‘Úsöwø©´ðßxù_ݲÜÿi\^ô8¼ûN™ÜÑRÙÙ|½l°rªxuôùÜšoš6ô¾Vöq½WîÕ>[Ñ“Û–”ßq}ujô¹nÛüt[ø¹úù}ûqÔ>íO=WMYÒ”zÕ=qt÷õwù÷Z½¶\ó~Xôï—r½ÔývŒÒùŽXT|î9÷ðî;ôvÖ¾ßùY\{w\½møûU[•yÐJ{ó~žéíõ-¾ÿ.\~~™œš\ö~ÒíÿÙ=ÙÔÒÌÓsR[4´µzûrö;øôSº›Z}3ÙüxÞò×XÑ–[YYv²rôt·w]ÝõøUØô¹Rí¹œtוöŽ×üSûò{~ñmëý¹ö9Ûý÷žÖ–6xwßùôp×øþ—]ût–ïkߪ÷ý¯8þ{™ÚÓý\~óZÝx›“ÛþÛÔÛu–o_n¹žyõ]X™üÕÚvÙZœt¿Tø•ÒtÒÞ·ýñìðyùóô·«WÔ><ÙsÜûõvÙ´ÛR^öòÝßlç™/t½ó÷Ÿù—˜|zرZŸ{yÛÑ]ý[7ýtkþní7õ:Ûp?‘ÝþÔŸsLÚvÓ{ÒÔ”ÞøõwÝq|<ørÓ®?|_P÷xøsTZ›“{VY÷qõöø3²5ÿstñ˜~ÿÞx-ûÕv”ÚuXÙ~÷¯rßmìüöö¶ÿ=ùW]YÑóù™u’Ø›S{’y3\_s}y¾øl;Ü~MŸºÍÙnÙšP~T™ØlóÖwûw6´^ns?[\U™õ}šÿ÷ÎÝ1ØÞéüòµ4sprÿsþ\ú’œ_úÖz–ºOÕþÙTlûÛóþq¼}´xo—]ÙüÕÜ_÷8ÌßïÍ^™ï|_tì4öXöðø³=øvۚܬÚTu—ý|Pr,°y¸ìöû÷ßówÙZr—rXŸsØ×>×^”y²úq{vôn•öý\–Óú}–\•ûVQÕ\›xÞÜú1ý*sî{]5õ›TúØêßÜÿ¾Ì_Ú]¼<±¹ìl6pòüétÙ¾ðÛ–«“¿þùÞÕÎzYýùXruuîº÷Ø—2œÝS÷ßÿùÖP–žØ[ÒtñTŸê5íÙqpœ»]WÙõµ\þüN}ÔOwóù;}xüèµþ~¼2Tš7Ñ]´ÚµÙÙZ“ÿ^Um{¼f¼öîðyv¸5Ðß4XÚ¯Ïû{WU–Z^“´xýv{<ìwóôÛ<¶ûOsüU¾ñØÒUØy–|vž8uêlþXïx÷þ¿×yzZþZ؈~üyöºò¿íë¾öwýp‘³]Y¶^uSÔU™ÖØV¬t>ñòúnò7vœ\µ[^ØZóí~Î_×ÔÚš\žµ´öÞm|ºê_¼xÒ¾\_t¸Ï±ÕÓUÖ|Ó÷_oøv:êºöºð2ؾ×ø-{Pu×ßPWþß²U¯xôp¶üéÜß-Û}üZü´ÚîQÕöÓXœ:¸óµµøjyô¸tözÛÚüûQr—ÔÕZÏ|[Yž.õøÛmzö”øô—¸˜ú~ŸÑÚpTLRÓóW{[´4wôî»øøžv›ÝZôóWY¼OSz{Õ2øspµôô,´V›²wÞÚrXvuÏøZ•_Z2›qqßoíµí›t¯XÕ³ÝZúú[ý\ÒÝ×Î^—´™t7\îyr›ßòŸÓ±QÛ|øY˜ÜÑÛÕO²S½¹yûõ¶ïúú4µsÛUñsRóÿÙ5^Óxû›:ñò|íuþòñÕ6¿ûÕ{›uýþvÌ•ÔWx˜°[o¬ýwsüð¹vTVxýþxtV»ÑO8Xþ.Ÿw|§öüÿ®=Ú|{TYu¿Y_ßÒWÍVÔø»6ðúë÷þÞ´ó_“p\ÝüòØßpTO¿R7“8öð:µðjµï^¹|¼øUùÝòÕ^ÚX•O|õ4¼yº³øjÝvóÓžoÒÿzþßWwÔÎÿÓ\]×4¶úùðøl²ù7¿û]—{ùßõØÎñÚÔRqœY¸®ôö]rüñ4¾XßmU˜¹óÌõóÍW’^¸™zwx«ÿòuõt¹ÝoXûTunÝ×/”ÝÙÓz™=¹õqòwûoüµm™|_²ÚÚ·X•rU×ÒÒy×ýqŸ¹1³öÜ^ïRuœÜMvÿ}ÙÒ\ÕZÌ~ÿÜrµ6뻼÷|vZ®Ž<ö^žvÞ0ØRÚ^”×p°ñôj³ö¾öú{þ_–þy{\q˜þÝÑÚSßük÷y°«w÷SkùzÕ;”Ø7{ÔšõwSÐ×VTúX»Þq±¿þr÷û\›:ÔÖ6VvÛL—ý’^ü]î?²¿ókX[p\û?M;z×tvXÝ_”—ÕWrüxkð¸î±öú}zö¿ÖñVYm¾ŸÿØÒX“ø½|¸ît´íðýóó;1Sø\txÔyÿ×ÒLß{Pu[ò5ë\î}™7œ}½ü_XÌ×Ï|ßõùlµ´ïý~Ÿöö]K®[^ûÕݽÔÐÿÙ0™ðíó¹úüìr“y™_vž¼þZsvSÙ×zÕ1´{uíðï¿vþúvšîZ]\´ûÝÔÓ_ÓúXú~°²wô÷/ú4þ_V¿yYû“ßw˜ÌPÑYÖY°·Ýôúmì4²Ø³8^û=Sðô]XýX——ÑùÜÛu÷í[ëò»÷î¼9—º7ÖzuVýØUÌÝ™óÝÚoßïz¹ûx²w–ûX“öoØ[Õ׿P‘{Uúºôôwyö°:µ^ÚyuÑ}m[TºW××™Ñõö|îžötnq]ž-»œÛxñ·‘ÕzMSpV}´øp/uó|ïÿ¶û]–+ÑÜxÿþ[ÓyS‘ÿÚv»ýïùoÿ´2u›wø÷½t|Ö·ÔØÓÜ‘Þ|ý4ïWíùô=t´ûVÝ´ýÝÔ~4ÌTüïÚröqøuôïß±]µžQ¼oÙ{Ö•ØÔÕ¾Üþøou›´ßïô;Ûñ_Ø~[ß7]½UVÒþÝ]µñnîšù^ªZz¿üÔœ<rÐÕÝÐùÖÑ«y8óß³w÷\ñÞo›o•Zí]ÝÛRs½Ùuðyî¶\íöÚo¹»òºV:][tß™Ö_Õßô]ßìž=oýmZ›szþU÷ÙS·þ[ßÍ8~Ë_±ÜÕð¸;œ›óÿßúÚ^õ»zÓ_¼~ÙÚyÌÒØûœÙ·Õì{8·ï˜~örù53~zûv—ÜÚxïÕ\úöu}só¹®l´wõ:Zy«]_}ïÝø|ÚÓ7=ñ_p¼œ¯÷m<x¼²ÓÙwØûÜÕÑWÝŸÖ^tÔ[¶_õ¼ýZÙÿ+’ØÖpzÖºVÛÖV˜:þÑvôz›ò7x^ñínÞù³ytÕžðsÞ²R3~Ù\Ú=ö~¼•«uöþþ”yúZp®9Ùqx•ê8·Ô÷þ÷4oNÿv™Y÷zY·Þø´^Ð~–YÛ×1·/þ{^ÔúT<Ô]õô9ÿ“|ÕyöÑt>,Ú~þQtñt´ÚyÊú°{ð\œÖT’ÿÏXôÙÜŸ˜ÚšRZœ_tú¿÷ÖØô¬¬qóðtþÝvø+].tpû6zü)øyòþ¼^ø]³óÖÜkôœþnU<Û[rÔ~Ùý_ߌ»Zϸw·ýZq¿›ú^ìpÙ²1qûÏðï™_wö™uÔôýØ{Ø\^øXظ¾QÛ^ÎöÚÑzq]yùQÏy–^îÖ[wÛvx׸^õ¹”ùº.ø\ñqïúwsµÙu0Ù7ÚTÞxôk™2ó¶²òùÕôò{Zñìù~óyúؽs›õ›ÚþX_Û›_þ÷üUŸ•Þ‘–÷Tš{ógòyœ×˜Ø[Õ[]ûo2î0ñºÖûzuo\–œÎÊÎR›}ðk¯or±ýÚPWr85vó{QÑU™»óîìl°ï\\–Õ_òöñ±r[˜XÞÙtôqoù6Ô\ÛwÞyu³9ÝÓQÎUSÛ~¸ou³ù¶Ú×ß<v÷òo–ÑÓÜSuö¬ôo¼.ÓøTš{²Þo76xÜMØÐÙ[¹¾ìïûöøY–Uw´súñpöÔ’Q×}–žrp,sÙöû™™Ôyê4õ|ï^7Ê]T¸Ü¾})xøÞü}›ZÕŸdÙÞwúzy|ør¶r[^ÖïVÕxì§ÖùµßÛÍ^ßõüû]¬|–Yú=¹™õ&3WÙZõýPÐ_ô÷Ü{÷XVþµyÛþ¦ê|•¼÷Õ”_ø÷²~v´þWyž0ùX¯e¬Ü•|{ÙWT[³Ýw¹÷ž¸Ú›qÿYÖ÷fl~Ó=>ÝÎ×Þýû^¾øôVU›p™WÕx'ðþŒ]5ÕR–Zöø_zõ¹\ß2Ú}ÒòdëÿüuÎWsµyÿpðY÷yxžhæ»{ü[Q“µøºþõ0u]Öw¼{Ò_èèp]ø_ÏÐVþ<v^²´ô–ß6×uÓûèhø›T]TÌ]³Üôô¿r~•ÿþxýP±¨§×ÞÜ_—ŽúüüsX{°ÝUü÷úW¶eìÙÛþÕÕÐZô³]r¿qÖ;ýwŽiêôuTÔß›‘Ñþûzy¿ûò™;¿zÝÖh¥òÝ\Û½ÕОòûzvs5úÛ6¹u›åët»Y™ù˜ÌÖø{ør×møœØ¼ßY¿ðçr˜UÚX×ÍÓ|ÿx]´´š×9ÿY¾Ùö"~~Ü—øÙQÖ»ÝóþûtñÙ}¿ü<¹Xw%nYü˜\šRÕÚtò_¶q¸ô¿ÖðwÚqê«ûUZ¼ØPÎ{žúöýù°ºÖù;ßóR÷éé:–ÕœZÓØY›löZ³õ{Ú»´8Ý]§líÚ]Y]ÑÓ]™y³Zóvú˜ßúß¼V÷e«QvTÖZRÖ}z»ºøxúVØ8ôÙ»ífº×^ÓZPQüºÝû75}Ó·ùÚ^|üæ±9ÝU•}ÒyÚ²¿ôp/Ztþsù»ûú¸æjXÑ\ù»[~ßoýÝó÷Ú=Z¶s•ºšoíòÔVþ—\ÑÛ~¹¸¹|õuøÚßðtÐòú¾gx\TÖX–ZQ™°ÞþùøxûQzršÛñß´(¯×غÝN=}œxñúöwÜû¼wþºu–ífºÙNþÿÐÖúÖ¼ózW°´]Ûp4×ñýüæ8t’”]üÓÝÚúx°¾}ª×}{ºyœyî×ëî7PYÙ–\Îyþ^r|ôñý[ºvßZï_½1ltÝY×>Š¼ý<yß±øzÔó¾yšjûXjîýÙלÕVÜWþ¹´{¼¬úZzvžq±y¨ðßÔ“ÞÓYÕþüöùvõs’ryþ8rrŸq.qšÙÍzWUXÜßµøô±z^ž4ýÞúqq‘uéwO×VÔT•YW»vø¯ÝþÚôß´pyý«3÷ÖY”x™MöÝüív9÷Ø85^þ°ïû´iÝÖR^’ZRÿÛºùù{®¸Ö3÷þô+^ù·jZ<ÏxÚÙœŸõþótû{xíØ^i¹óX¸íùÜYOñž}œÒmº=úØsØtö[wZkš½ÎžRÖøÞÛßßôõúóÖ|ü|¬tÚêWr´|[þŽÖ80žîYn{ùöXð|¹÷ó»8¹ðøÖxÛP^ùÞ™¯\³÷YzgÏü±{›i×v¸þ|¼YÔÎ6ÔØžœô]]tÜ7ÙZ¶ÔÜløÖ*Ww›VTRœÔܺû]¾íüó^1ØWu´-Ý5ö^ì˜0ÐOxûøu“mïÚm»øöYösžv«ûþoŸ÷ÕÜSüwÝZxò¾m¿t}~ø4Õ±í’p¶[\v^]X“יܰ”úò=\]—ÿºOïÕòVö_z]œÒ™ØÓ’qZ~ó¸ùür˜ú¶ßýsúøqô´þÑVÔ‘^ÝYÛ½r™ýï÷·—jþ›îþ^²î>9ØxÞS×tõRu[ÿ4w“çýôòö8tœ¶Ùnõ_v^ô™ßÚýwšZ´ô–÷l’ÜïØYðÝwþ[~v•Ô8Ú•Ñ~ØŸµÖRpØw¹•uø9ØzøõqU±ýpšÞy>ü]ÏvÛ”îÜ;]wX\º}ÙÞô}›û_ßû´<½ò’tëVßZ¸vgžo_rúøn™4ÝÞ}\rpµø”Nyÿ·™h›}·møtl_ñø¬¬úzl¿–²þßùØÔÿÚ–ZlÚYvŸUXõv|z÷ÛÐÝß•ýÿþÖRØyº_ëwùšl}/xÿýW»ûvÑúÕŒ<ÙSüÌYÚ¶N[ûÞ™ÿºû›|ÙÛ:toüuóp¶Ymröö¯w®úîv¼wmóþ»8öÖ~´rѽqÞºxÕÿûל¹ñ^[vO÷=žY™úÕMpÚ|~Ë|U[×›}ٜӘ{—X|ÞÞR~Ø|–ôWž(˜l~ßxëý8îÔy}o¼·óý×j¸oôöÜó_úl²yýtö›tî<²ÝÛtÛý–²ýÖY|¿˜öWÞûÿ\Ñ1›õÞr“ý¾VÏ;wÓÞÞÒùvÔØñÚ\ôü}½?VóúÐ×|wß“ßXÔÞú¿nŸ}ùì¯x–ô«sð:ýüýtpþ•ÕóÓo³ô}v¬lÿ±úrò´Üv[õ~6XL˜ÓÛþYÞw¸ž{×o–´lxxü[¸×·T‡VÞ|û׿þßxùn¾ÿø|vtnž<žvö]½Ð×ûtÿ\´ìuú¬¶W{ðk:ñôÞ7uÝØÍÛU[ÿwRµ°Ÿyþûþöê¶ÚtwpSQ‘ÙÝýz›zºvÖ0rÐòóÞï´Vº²ÜXËÕÚÛ:ÜõØXó—kœ^ö’úîtÿ=wºþZXÕT¾nšyùþírŸp¼Tùg÷üùV¶iøÐNT9¾xu›ð]óo˜½õ}tuw¹8{kõÓ““Ô˜tûÜw™²úÿòÕØóÜ~0°Ò¾èùNŽÕ˜<Ú{¸Üúð¾ó‘xzü+ÕzøêoÎÒÕRyÛvyÝwŸv°yY3|Yùjš/ër’Ë_×|º{_üùöûüüžþ¿4³SuhåÕÒVÔý:{Þxø¾õt¹¹¿{ö<þ]ñ!öÒZš\XýtÚr{Ø5·ÿw_¶úÜýýh§[’QØZú˜÷_ßrýq¾Ür}ßØÿÛ¯e0“V‘ÝÚ»ÝrÙüùµsÙwôÿ]Ó²%îUÖÔX’_½üÿ[óÜþ3ýú0;ؼ–¹æê^ÓWÚÖÜÝxøy7Yóø¶ö·²úÐ|ùmä³ÔØ|xýö›y·÷ý÷q—U^nkìÙΘØTÓwÚüx\Ûð¹žp±üYÛšµêg[—ÔXÖÕ{|ý½ÝÞïŸ{ñ2“|ÝXëéöÚÓ•R|Ù}¿\sÛô.øÛß÷ä¬TüÖÔWŸ|ûÿ\¶±vùë¿×µUífòW½¾×T¸þ]òûWñ®ÿwîsTÞŸ±(íØ^[›Õ‘ÿÙßz›õøvòwÓ™]¸éhÞ”XY•ØšûÚ{ÿþ÷v¹êw’Ûý9ëf—]ÓÚW˜]ý9ÚYóûü¶p—Y[ùéi›ØUŽþÚ\™ýúvt¼µí’ÿÖõêl¼’t[Ô•Ûß<[{ú_m»²²ð¹ÚþtçìöÏoØXØùÜ¿¿¸¾³özslšZßðélÚYxœÒ–__Tþ^ÜÚmß_²u™Sßgñ›’zÝÒUúQuÝ[³ù¸ò6ZÜ]q*k™Ôöþ“—\Z“óÞ~úxùï÷‘ÿÝ4ën—Þü}QÐ;Ô[žzùÜ·rötn“wÓð&±•šøÛÔÓºœVÝÿúÛx9ø¯\×»Tt%/ÑÚýÖYÛøÞ[7ü|w:òr{‘¶\°æqVÛßY[»×·Z¹ýy÷Þ3u.Üù|®fuTYº“ÜÛyþØ|nWõ¸òûqWxè'×þØÖÔÙyÜÕ8³×¹üõõú•Ü\þ,¦ÕZÚÔV^ÙÞY÷^·~œo½TÕÚ°éð×~ÙÑšÖÛšõüúßqµñúnåòSô]Ñ›úÕ_øõöÜïxv°,žùùêgß>}üÖVûÚ¼3{ßwÿnõÕ¼?û믖ßúPQ–ØÓÞÛùÝ;ý]ó÷ØÝÖö©qú_M˜ÑÖ]¾žõôßpï–üßzl©_y—ÓÚÓÔØ6¿üð7wl÷Y´úwîhúS²[ÒXÛÜTœõüü´qüñý›²>u%ðY?ù×ÓøßÓ=øö~ùø±5|ŸþÜsºç|Q´ÐÏûW”UsœZ´vßvÞÜßX¶íîÛÝ]¾TV˜Üü·^óþo¶×s½[vjnÔ×ôšÕY—Vy·ÚtsýñôùZµ¸u'÷ØÜ7ÚÖ[ß›Þzñýv°²w{û<u»íé\T¹üV•ÙÚVœz÷ÚoŸt·Yûß{š©èZÒžÚV”VÚ[_ñ¹x»6rX[õöžêm”Øü™N×ÓÞ˜Y½rý}µp™ßmþ[sjtÞúQ—Ú‘ÖÚzŸ÷»öôqßù3øønp±Ù<xT’\“×Ø}û}ûëýé_¯mÿ5^—\Í|ßÏóßn¾±tp?øìôÔêí]yü5:™UXÖ°™õ|ù{õÞúÝo”¸kZ[óŽÝ]Ý”×l×yöt™þß6ù–i»×í’ÝÎ\“ÔZ<xÛõùzÛ5{ywÕmð}úúÔÏ4Û’|V:óßr¹-Ú{ïó>Ü&þvØyRzÔÔu›q²º³þô^{ê½Yôjú3Ú\ÝØÙ|×Yºt¿ó»žëÕtv]òÞ~nò—4ÖUR˜Óûž¼ÿëœþÒoUUõ9{[ïZ›ÜÔÏÝЛuØô0ö]»ž°Öqv[wññÝÞr‘Ñw”R~7ßðk÷žønš7u¿v·l÷ÞtÒ[~T^Žñ_u¶3Õs¶_ô[tµ.\þz¾Ò“ÒÙ~|´ÔpmÿzTwVqÚ÷nŸ~ÙÕÙÉÛøU~÷›,±7zÔú1xÚêÞêü—>QX•\”Úµßómÿ|ôVsÿ~Ûòpÿ³™Û•ÖÒ_ºµóYôêxºì÷|îw8uöû·9ZUöW½Oü›öõ·žõqö×t9üûßúð_¼YŽÛÖÚ[÷ØÖ¸®þ3|ÕòßüºRRjœþÓ\Ï~Y”Ôóv¹ô´xÝ™oõš|m½¸úÞØ×\Ø¿Ñtþuÿì7¿ùjýÿsìµzwµ|X²³ÓX»ðÞyëÕ*²úõ^8ôþ<ið]ÞVö™ÜѺRyXwRìósšU¶úþÙ¸~ÙuÑÑuUÓÿÚ¾ß]Y´µ9›/xþ•-»Ö÷{Jsz»XR½TÙô1[ø2¶8XtÙøwn]ò0Ö6pÖùýÙzú?ð¾®ë×ós~ßÝ|ùû86y}òO^¹×ô—õßí9xëwxÐ5µÿq[´ÛÜÙÜzRZü—ûô1ßU¸}Ü]ÝoÛ×òßÔX™ÒLUVÖmýú|S¶W9¸{ûøÚ¾7µôñÕù~ÜÛ°o²—(òu÷q0p´yžo]¸9ð_|WOYö™³úyê÷úî÷oÛÿñp´zñy¿~¾¾JÜØÛØ{¿z²úؾSûÏî×ù9ØŽU×S\ÖO÷ÙÛvt¹\Ù“÷[ÚõŸ[ýÛÕÚY•ÿ\û¯Ü2ê;Ÿrú{÷ý7ï26õÔßöÕÛ2Tr0ññhø|ølÿØòÝ90ø4Ø>YÕ^Ó7úÖn÷í/úu¿Zí]ÜZu··u–WòM‘V\“nÿÙçÞÜû=Úܽ÷Zº{OVטÍÝØݲ²ö0õu6ÛòYXðñ\½÷tOÿÖÒRÝ}¸ðlo½øî8“ú{9kVÞ÷ÏÝÑÐœ•Þrt¯»øôtÿÕš]v¹øq]Ô–UÐÜx²ªs÷õÿ/›•vð÷\uíØT—ÖZT÷túïlöý´1×XuóÛõ83Ö]Õ”Ó}¸rú1oÿ´Ú×qøZ³¶¸ÞZÐÍSþ_÷÷wovø·Q];µÝóþ˜QÊ\Õ]ü9±ðq[¸y×ß[òs˜qx·ÕPÖÓÓûÝ¿/òò´wý~Òí³>öyj‘ß\PZZþóºî³vö_<¸_›+ôùvwØÛNX—ôÝsp0ùòÛZ|7Víñqœ~<‰[Ø“î–÷ðrüû•<Ü÷]l¦ÜYÚöÖT•þõùÜ{õö\•Z»÷ýñ¨xTVôѾVßö´´_øXÜžvðÿ³$ñOýwÕTWv¹øøz^{Ýßÿ·<|û©¤ùŒÙóWûÔ6œrv{ÜŸu·[÷^v·æî\”Ù¸yÓÿÚx³Ýq¼üxݽvœ}´x©ä»ËÞŸ{ÖWZúüõ¼Úü[6ÚxÜ^d¨ÖÏ^šÚØSy}¾ßí–|ùÙ]1™<údè]ÑQœ~–™Úºww|ùýûÝžuž0ûÕdå^—T–vWÜXºvvxxóÞœ{³¼wZ|¥èøSZ¼ßÔR¾ßuû|µ6Þ^øõÛ|™gêôÒÙŸÏ[—ÿúyxx÷—6ŸòÚYÿçm·ÒÞ[Ó^–Ýúõ^ÿ0üÙ³?|Ú©åñ\ÒÝü××]÷ýu¸rø{¾»t÷ÜÛì"ü{–Ÿ¹ÙZÜÙºüµþ_s\Ûw|¿ž˜\fkUØ’ý\Ó›Yžôú[õö\Úzþ¾\ž¦«øØQß\OÙQ¿ûû_wøsÖ:ûúz•¬dq\Ú\Õ_6ö=÷´xY¸wÝ÷Ôs%ëX–ÖÚßݘóöu{}®]Ý÷¼²~\~æi8YÒœ[}Ôײx0>wtwÜ|¸s\\©èïSÙP\›Øôýö_»·YÞþ{Z”ÞepW–OÝSZÓXœ¹¸ßÜ·_ÿWxÞ¾ÙþkgÝÑÙ{Ô×Úœq²œøí}úv¾u2ž_êg·ÜÔ—yÝ_ÿ[q°s›o½xvÞvº³[ók«>TOzWZ\ð{–q˜Z¸R{œ}[XîiúUÌZœ\•_q_öw”¿_š±{Y~ød«ÒÖTSöÝÛ|u³úŸz¼úÞßuÜ·¿¼æê~×PÏ{[}øüð-ºš´zÜú}»ü2?qäkÏÚSÚö¾Xóz01Úøö›ôúÜ;³—±éÿLRßœÜ_ý¯t™ÞvZ^ý[ÛóÞj«ßÙKTÕX^ü´3Þ|º[ýýœû>(j÷žÌL[YØØño]vøt»~ß´tpÜðdí¾RÔ—ù~Û›3òxu¿øù¿Zß³\um¨ôNQÒ_[ÝÕ<svž¼»ÚuÜúºûÝì¤0ÖнÙÞZÛunþ]ú]žûŸšÝv<÷ejÙÓ××|Øß9î0¼ùüôoÔs»û½iüÚKÜX\•YضóžØÿv›õÛ²×tmëÛ¾ÏÝоؘ5ï]vÛx¼tØ^;ôø²hìzLÍ^w”Ýwírü:ÿµòÝ~û²{±i«ÛÙPÕÿ~ÔÞ»¸nüÜZrž›^Õv|}-çZÔÎÛÝÚ”U°pý~]7´ŸW»·vÜíè2SÓÙ[ÙÙømð~œð´þ\½{¶ø³çyxTNØS½Ó™Xï>ßw[þòÖX}²œùljÛ;ŒÓ»’šÛþûòºßzþ}¾ÕÞõ~v2ê2Ö•ÔÒzÓ×pqö\õu¶üüž4nÛ¬©óy[ÔUÿÜ[6´ïØzöý7ÛØ_¬Ýý.nxØY‘]ÑÛüößñùòܸíÞsyªõ»OØý”TÚpy÷Ýõ;¹wVŸð±²ònzYO>W|P’yóz{^ô}×X—,XÔ´í:ÏÚÕW×]ýúûùv¼öUy²rÿqéø×ÓÜØßÜÙqùz³õwõ¾ªpú_´®òŸPÔ}ÞWþZôúvßò{ôß}Ý%:·~¬ñPÙzÙÕßžõôy¹²÷ý_ño¹õÞqn_ý“üœÓž¼XùxÓtùuÛZÚo7uÖü®s]ÙUÖÚ™þÒê“^îßÝ5öÑóÝ}ýÜÕÔØ·ÒtzÙÿ³Zz_qšqù5óÞ±›<yÖÚ_¼ÜpZ}ñøZ¶»xRúìØý(vÖÿ8ßXÜ÷~~gš¹òð/žv3{ö¯ò½qÚï¾~°ºÓ1ÞÞð_øÞyÛŸŸxÚqòÒyñÞOÜVØ3VžøضZ•×ð•ÏôYÕ[×]ÜÞÛг—_RZ3VÛóY_x½Tr»]úqóz]tµ¶îòšÞúvipvµññ¸+ý´pyÿðwötùøí–9éŸ{õÚ|24ÐûîÕzûÔ^uœ•—ÞÞqR’ÖöVÚ¿}ÓÐô¸tWÖÞšøù[ÙÚÝÚ|®Z|ÙÜWœ3WÛmPÞõØ’mþÚ\Û¿rZ[´Óúõw¿ÿ“w½úÚqû¿ê–øõl¾ønÚjøoý¶[wµëô³~´ù{ö¯øìûì[óz^ßm¶}œÚ–ZòÖõzQÝ}ÿ’öþÙtR—ÿØñTP|³ÖùœÝYýV^Ý]UŸºS¾/™Yë˜÷_vVô^²y˜ViZ³ôõÙ{8ù~ò¾~Úõ}û|ß•Ú÷úô0ü]Óy{ùú÷ñõTþ´ûxZݹûžnYQ\sÕÕØÚw›.ßÔÓ9öVs™qmÖ}q+ÿùî¹ø°óùs°ty+ùzw°nÑù³:]øðûUº·×ÿÔT™WØZ±ýÙø“SÛ—xóY½ÝÐ\ÛYvpUö»TÕ½ž>ôl÷Oo]¹\™wztXÿÞz˜ß1ùïÞ0<ÐÛ8k—¼«ÛøõñzÚüÑ<ôÓ]¯ñ~õñÚ7Wøþvü~s¶uõ”ø«úo×TØ2øxòÙž|ý9»Ýüòz‘ÔØ[Ü|êÚÚóúT™8¾×ü=ɘWø™œøÿLÑ}2U”ñºÔÚùùYtù¾þt^úñ±[šwúY¹m³îÛ/^ùtpõ/\zï¶Úzïõv¼tÛpqùöþ·TÞz]_³^9™žÙüwútPû^ŽY¾VÕ]ÝŠT:ØZÚ]þšœýý;Î÷Û_íÑ;ÛY_™^tôýR7~Ô;ï[–ßït¼þ,*wýpm|÷·ïr3ÝimÚô±òžüìwWm¿w_øðÿ—÷}–<÷T[_”ÑÔÿ^ÕrØŽÕüÝØVžØØSzºVð™ÑÙYõú×û]Ÿ[Þw”5qÚòös;¸ðÿ}öW±pô9ÜúhÕÚo¶>ÿm·ÿ;ïÚóðð~v²^ww~ܬ³y•]qÖ¿úñtÒq–×½ý:ÍÜõÖÝÒ¶õRý¹—_ÜÝ0×x¹Õ¸ñÛ{ŸqþûÖòòÕ^¶ûÜSö8óy}|éxÜÓgxž3ûû¿Ô]µ~_±Û»Tü™œÝUžÛÏß•Õ^³UÔÒ9Oú™^^øPÏï]W÷VtºÜñ¼U÷ô¾ºt³yy÷rÿ|t®ïu¸n½rvp³×xònù{swÑÞoßµóy¾™ù›ÛtUÝóš]½vßÎ2÷ÖÛïXÒ{š¼üþÙ_Ö›UÝy]˜Ó3OÞÙý:üøÒ®[˜ØúùÛ;J&U[½ùyÞ}öúµ½=·xþ8ø|ö×ìp’?ô[üÔíWöù~Ñ®‘pÿ}Ï7Zõµš_~“ëß×s×yüVìü4õ[ô¶¹Zn÷Z4övµüv6¶šûsØ42_ïzÛú^þÕ_/[ðôœ[º•[rß;zýÕWVóûÜ»²WÒÔúØYt÷XS¶|ÔÞóXÙ¿^Z¿rúZÖôØüëîôxý9NÖ7ÿ·5o¼×šßØPÔúÕùí=Ô“O~š\Õ[õo]\6´>öhíßü6ô^_±p³ò¼*}ý¯îoïÞ-ðõºñõq|ôòô¼ýqû^¾]·Þß{ŸÚ^{–QœQÙÞØÕÒÜÒþTÓœ<ÚÒW÷Ô”ý»Ò•YYxý×yö”œ¸{Û\-|î›ršqp~ótõq¶øt½ð¯^òšëY}ë}ô5]ôsu°û^yuösxºZwôú—ðîßÞnŸ_žŸ2üîØTÝ}×]úÐ|‘þVÛúÙZ‘ØUVúû”ùÞÒðÜYY;sÒóÚÖë×úÙÔqþÝ~µÞÙ]8ö»\0ü‘vv¸öúêÚXoí|:½mßZ$±ü}Þv×5ò´®ÑûÚ9}|0þßuÒÚo_w–÷¾TØoÜ\ºRüÙ´õÝOµqýèßÐûÞ]úìþ~·~}ß¾ü,{ý<Ÿ|Ú’*ÖÜôôTS÷Žúý¿WÔ—yÙWÛTw–ÕðýÒ^zÿÕSmz¸™5<¹uÝk÷<8nzvéjûú¿Þnø,Þð4]ò}zûó^þÛÛº÷Ò0¹‘ÝùÒ]ýÕÑþ\‰¸ßÚ;ÓÞÚ—²×{Þzþ™ÿýVW¾4yÿñ—6×xïQ´ñÖüsÖÜö|ý-4õVõøüsìظùMv÷Ý1ü¯ýÔzú¿î<Øòôöé»ýó_o^ñnût1™Ü_Óò{šoßÒý÷Ôô»ù]‹[¿V4ÿÝq˜Ñù[ÑÝ|”ž]Ï{\Vô·ÓØ~Þ’–v‘{šX[>WuùÞù1û÷üWÜy°mðïªoöòZ4þ/°tîî»}ûßv·ºõøøo_ö¿pØuöüX—¿›þÝv[×ývÔŸ9ýӽͿÛ\ÙôõœÜURÔÙMü~—Ü\”ùÒòÛ~¼×X_–R®ÙÝ·ØòÑÖsžÓò^þ—úþÿ×q¼Ö¼qÛ|ßó²÷ü/²8÷p÷ßïW,~1qõÚ¹óœnòûwYxø¶6¸túpw½÷ûÑn¯R÷2ž;ÚÿÿÝ\õÛp>×ÞØÐ]Tp–N2×ÖùÔÿûÏÚÿ9Úø¹2Õ\sØûÚø¶^Yõ÷Õß÷’2q}×2Ö8œv¬íñ_¹ßzXnó±ÿ[q”Zò}ï=þpÝøøÙ¾ùR[Ÿûò•?xŸú“ZØXÝ÷¶SôxÛÚ‘{ÕzTZÚßÏ¿ïØÕøÿ×}œð3›ó¼úòøï±tzš¿ÛT\¿?ßüÊÖkQùü’Tõ^6wUÞÑù˜R^ü”sš3zqÚrx›þhîÖ2Ýsüzó*t¾7ÐìîXlðòmöpp¾2ú7ó˜ºüñX×o~ø}>ýÖuÝÐÚZ[s×Ø•Z•}Ý¿˜QTQÚØzØZ_VtØU³9Λî;Ô3|{w³Û¶öòØûôöv½Wûùõÿùž<ÛÞñVwx¼w¨žîòÑ<íŸüpZ=oý9ûZœØw¾øzÖZ›|ZÔö6ynµ6pןîX^åÔ÷Ü»þ2^œÞXÿýZnÛÞ¸6;]övÍUm”Ô¶ýWÜ~Û˜ôxŒÞÖúTõUUú\mï9t¯¾t½_Ü\m’š«Þ{ò×Ú<õ[zu®úýémü÷ø^´ô¸Ûøøú4ž×ïÛY_öÕ[—–?–~Óþ\ÓÙ[WUœ{Þ”_úZ·óvMZôY^}¿Ü´öž}{Ôîßµ-¹ßo»~þ?zól^õñ.z¯œ}¿örqnòq±t«VòtÛözŸm¿»úqQ—üþÜÐ7UÚnRÒ}´_ÿ6¶ØòsüøÑ|{Óðv\ÚŸX½Úº\W]ÚñX•.•KõžßÛwÞ[Þ¶ØWÙZ¸ý•þóU}ß\‘úòrØšêœ^ò³óÙzônWè¬}óó•\³ÿýrº5~û¯øšùòY_ór|™uÛöþ\Y–ZûÜÏÜU•~Û°8׶ݑôk\;}ìz›yÙ=Ü{ôÛþ[PÓ¼Ïr~¼ØÚë¸}8.ÐírÚÓüYNžùݽnõ´ì]‘Þ<î~6°êÖÑNÌJZ_úý-ìíó6u×_ýô®¹v°ªô|ÔPRRYý{:ñqöýwš1ï?qléxUÓWR”ؾøtó¸úYüüß1ô~¬çwßÖÞÖÓ•Ôšvõ6ú{»ü^˜Vw¼ø²mèß×ÚT’ÕÐ×r¸öþvºUš½û½VîcÛ¾Ö·^ØuYô·Øzøûÿ_ûß7w.lyÙŸ¿ÝÓÙÖv.Üöö›púŸúŸ´æ*ûW}Ü;ÑÒÝßðt|õÜßs–2›Úñdê_[üÞÎ[ÿ¼1w~ÛyÚ´zÞÛé%Ü—Ú_ÔÑß»zvyóý¼9w™]säêUÚüyÓÓß\óûùz~ü´üšÚ¨c×·ZºY—ZÝÚxº]·ÝÝÛv<›]Úuêí|Ü¿÷Ð
ûY1uðø»{ÿõ7›¸W0#~~Üþ\Ò×ÿWùzÿùy[Ý¿·þ[»ë(îR{ÿÏüÞ}¶øöÙ5»9xSy«göZ|öÚÏTØû²ß{Ù_x³^ø|d©Ø™ùÚÑÎØØÚrzžø¾Wœ_ô=ÿ˜ÚìcÞzÞ¹ÚÕÒߜߴö¿{Ü^ú~÷»þ•é&~Þy~•ÍÕZÜ=òy¶ûÛ_ò¹=ó~[pçª;qÿÓÒœZ|ó{43Yy¿u¶y|:ñäê×þ~ŽRÞ\]6{÷ž^Ü1>˜ú~Qëdò•ÝúÙP[ù^yùø»”Ü4÷UºúQ~&ê[ܾ>NΖØÛ»[s[þÖ·?]ºüÞ”®dlšÞ÷×OVZýÿõöw|ŸÞ2º^ó|X÷$æݼßÔOÚ\¾ûwù;·¸4x5_^æåóÙø÷UÒ•Y}<ø¸¹^ܘzž[ÿ›R4fïÙW·ÖPMØZz½zõž<¹ö{ûóØt£³ûÞwÜV”Ô}{Ýôö¾ÜÞzú¾>žÙ«eòÝx×ÌPX™<øu½ýžY¿úXºû’ôäi\Xú]ÍM˜]]ó:öÜÚ·úþ¾8~fh³ØþûÕÎÓþ›ö{röyž<4ö¿ty\®cº{ß÷×UÓ}Ûýºöyûú¼yúwüäh<ÿüvÏ‘ØÝ~6rþß¼¾_~\{–ìc_¼þ½›ÐR×}ÿxöÜ]}¼~]¼X£ö\}µÒUÚÜš|¶{^ß½Ÿ|ºVy¦êݽñý’Ï]×Û_¸tùÞùÜy´ÿ4úÚkd«VôóØÑ–^Ù™6ûxöZü¼Ü™·S^¨æÝ]ø{’ÑÓß×}»y÷Zù^5¼xr¾šmãyü~÷TS¸Ü×3ßóž~¾_z¿øYªfyÜxýÔNÕZZúùøZ]ÝÜ™»_ûwäèØòVS•Õ]š¹ö5û~›üÞ¿üU˜çhvÜt¹PNXRßûýÞž]×¾]ÿŸYvãµxº{ÞØQ]Ú™ú·ùöÝ÷¿xüò¿Ÿfçv÷u¶\×ÝÛÚ;Ÿð±^¶yû¾wx³Øq¤êÜz÷ÖVÛÚ\õøß¼ÿÛZW¼W›ìg¿ß¹ßœMÒUW^ÿýt|Ÿvœ]Zü¸ö¤è|ÝüüUŽWØÒvú_qœX´ÜQ¿~×o$²ÓòýÖÒYÔV™üo˜^ó\¾Õ÷žØ¶æq5ºvÞ×—ÞÕVùñövö:ó[7Ùÿ{¼ë¨ðxr~Û–ÓZÔynÙ8s]}»ÕÝû—.*ñüýñ¿NÖ˜‘•øþ\üvþ?Ó}|ZÒ5èiû¸uSßR×õ;\øq|sÝ–ö¶[z+èo×mþ˜SzŽ”q¾v÷ûø´Z¾ÚÛ}j7óÙo_½PÒÖ×پ߼x±__Tw”W¶rònÝqqÔØOßXÛüûz±>ÙØ_ž”vìø~;wWWÑ’öVßù4÷÷š~×2Ýöö+ÕmmxšÕ|ØÖÿú²ró7oþýxzé땼¨pÖWÞ]×^|¾³óܼzßqTÔq©ÜÜó¹œÔŽY”Q^^z¿wõxT¾ñÕNóvÿõ»»×•ÍWTÑŸØ™óö7¿Þ´óÕ~øðÿ*yöÞ]zÕŽX˜Wvvû¶ñ®y½uÝü.pÚ6gÞýú{ÎûØ?tõr¹ðóZòž]¾ï2_qnÚ»—>ÒÓÜ{ÛsŸ«[p||sÙVnß±uø½SúÙÔ˜ûßÜünóÙÜöݾ]¾2»WôVÐSPÍûÙ[\óöX[±[;ºÞ›ôòz<±TÓ5ÙÏÝ~W²óùøô2·|ðíÖ¶wwóß÷¿¼ÛUý÷UsµðnÝûù.¯Ryì½_ñ~Ù0p×ñV_ß:»w}xëŽð±yÙ¼÷»Uó›Üÿ[—}VÝxÿ˜^ñR_5öžœTsŸ]÷Òû_XÝ’VÚš{SþÔtßVwöS5¸Z9Ùú÷{³\÷ݯø°ßýï=õ4nÙeÕ:ò¹ÙžóøÓõ{ôÜŸ±ôûUo{}øò_³ú¯Tê|;¸q·Ös¿Ù®Õ¸÷ÖZuôû[ÖôZ3:[Ö½¬·|˜xxTðõÕù\Tº~”Y‘žÙP¶^_—øíÜÓÛøzY0oß|7üpÎ5p¶ôú]OûWŸßÔÐ=TN{T•\ÜR{½ïÓ3ØûxØ·0ÿnw>Y?®Yó;vúšÜÝïý4°tñ:Ùïþ.•vöôpüòzXíŸ|·7ýû¹Ô”iÒxÕÞW[9—ouÜÍýïÛ?{×Öxº\ÝÜÜZvµÖôÿqRònœ¿2lœlsvu÷é«òÝzÚ3ë|œq/ý{úsùÞž8Þ<sÕ9]\Ò}7ÓU>šÓ]YúÕ•WüÔWûЮ‹·Ø¾RüVs×6»×ýÿOð×v½Ýxúœp<óÛ\ùðw”ïYß”uºnÛôÖ˜Y|jû¿ÿZzÝ}·ÿÜp>tnžPíWÞÛ·û´mü2hÔ–ßïk5võú¯|ïu[hp-·8s»yß8ßïZp:oýZœ^oØ]yØÒ1˜ÜÝZþÝZÜ“ÙS•¿UÒ¿S¾R^þ~ŸZÌÿ÷RÜ:ÞßfZÜ8?x9Ùq°r¾ßtì”õðpv{pt¸™´tð]²öÔxYyôŸñÚ;zò\Þú½ö[o™:—T™ûÚš¸ûØöÏÜø[·’ÿßÙõWY±OüðR^îÜp—|òX”;X_UÝñP_ºÐßûž·\_º’^Üïôü.ñXþZÝ\ô5Ýï|ôµÕ2TîsÝÙlhš8röÙ~xü¾uë²Þ=î^Ø2ù:Z6\^5URÝÿÓÙú^¾µ4ÓÞïÖ_üðs¹jtô´ìß³±5´énÿzßxÞ\÷zØÙTžù~u5øsÙ¹röÝ™ÞõÐœvMÈXÑÐXXÜ3»ù–ú}õ}¼y¾Ü—ÎHÎÓ’V½øópë÷ó7ZZ9´ñ²øøÛÜÎ’R]þõkîîvµ¼¾Wú÷u7ôsÝU“QÎœÝqmlîwõ¾Û^úvö5stwVÒ“’ÖÝÜu¯o6õ¸ÜÖŸ¾Ûû{·|\QÍÓÖX|õo±´5z˜þöÜ›qðõ™WÛÌOûßovk¸t¾]uÕ¿÷t·ÛÖXŽQÒžÝwyªówW¸w¹×ØskÞû˜\™Q“{ßnºìèuØþñµÙ—pl·ØšþØ“ÖZµ{¾mí|–þût×ÕzëZúWßRÒ¹ywvï9—ôtþNxj·ÝSÙ?SÓÚyøó½ê·–>^ôtTÚ«oý^˜ÛÿÏ\þó´®ð©þÓôw÷\’-*˜XžÔÐ>vøõòmýÖ½³›V_jö|ÒÕÜÑ~Ûrz÷4¬Y”˜ú3}të{ÜO9SUÒ¿ÿîßð1{‘Ü”rØêü±ÑÓ’<_pr¿lóTØØuñ[¸mlp[ÖÿÙR?ß®ðïü꾶wôþ¸ëöûÑVßXÒ;Ÿëòþ¸Ð’òô{Ùq±ôTÍØ×ÜÙ/·ý¶{™ÞNÙpnW¶õ2}PÏ6’{WµnöY¶~\™P~kôyóvšÏœX^öW®ëß\zÒRð(ï×uvZ’ö6_uêý×|¿]—”ú$pÙ}õýWÐÓôû™qnõóS¸}O|³j°ŸÜ6_ÑÐTþô8üìñÿÓ»}Ômfý¿\^õÔŒ^úÞ2[ðïS™»ÚÔÔìæþ×·úÕ»ü4:uðÛÓúžVhåxžýýU|ùuz´mø•»û—Vû&k}ߟù›ÑÑØút{ö-|XÓõ>‘Óö*k?zWÑOÝ~züîó˜ú\Гgu÷[w¾ÖQUÛ±½8møz•ZµÙ\®§³ÜýxÕÏؘ5zwõõÚÙ]”Î7knùÛüõÜÌR™~¶Û°öþÙ]ºÛÖ©çøôøpšQZ¾w7yð÷Ýú½8“Wt$l]þýu¼Q’_8ºþòß}˜\»ÖO]êf?Ü]µ_×O×þüvúÚ^XœŒð#ׯӵwÞN{q¸]ús×÷Wö”W~ê°ò×÷ßvÞÓt·ýøµü¼_ÔÖè&y»Ûó;ÚQÚ¾r{zø·Úýü>ŽÚñ%r~ôy\P’Ûsþ·ß»Ø~[L_ä«Uy›ô¹Ó’^}öþ¾ú¶Õþ»ÐÙ¬äõ\]ø{×TÏz|¹ø{›™ÿ\“Sõe¼ßzyÿSVØù±>uqûÖ2ºÚW|e§÷]ôúYÓ>Ypµ;ypÙ^µ\“[r&ïû_ÿÐÎßúÝ;žºÞÖ¼ÓRWi©{W_yRÑ·þŸö]}ÿ”º=Î׫ênß[úúØÐÒŸy·ywÚ™xþ–Rúdçy¾ü÷TÓ}½õ:8uúš;úUØçç9v[ö|ÝÒ[¾4·{ötþØtz”’îgðòÚ¹ûZÓV×÷ž<y{ZÿÔyk¬pYX9]ÐTÚóv|y×9»Ò]näîš?zØÏÙ˜9¾ý÷ý[Ú=\“O4(i_ܾ{[TLY:ý^ô|ú•>º[Q×å¨p¿]øöS×VŸtúó¼]Ù³W¾íj.|»zÙPRÛ:¾[ñ{^˜[»QÕj¨ïÛ4_úQÒUšøwû·Ø›²ÜOîièõ;øp_–šžyózoñwÜpµzT¾¦¨þxrþSÜ”›ñ\ùí_Ýû›Ïïkmö{½rÐÖZù[wù:ØYJßôkÝ_~ÿÍN˜V~˜ÿ7|ÔÕ|¿‘
:+n·œµúÖPXßþÜñpßV}y×qgtò~µ¸ÖÒÜÖú›ðòtþÙø³Vßj§µtr;øV”ß|Ú61ryÿÛïÙYµê«¾ôóüßRÜÖÜzôõÝVy~TŽö,oÚrxùÒ•ÑX›µöýþYõüoéqyðó¾×˜UÙlò{ý|qú”Ÿ«ªvzïûúSØ•ZÖûù¯X\xóÓ_ϸ.®ü+[+YWþ–SôWóöý^q½Ôü챺wõm[ÓÖØÜó{ž}ùxšÐsÿ®ÞýõñPÓXNÓÞøû•ø]~×”29rÜ{µí’ÙÚX–|Üpo™ûñö˜Wìísûrñ/U^ýÝ>ípzúsüûÝ+÷2y´íøW[غUø°÷Üù?ÍÙpò:_sýóÒ›XÓwUóx»X|vŽ\wõs—ó÷wÖV™~S÷·ûßò_ñÛΰòö›y°nTQÚßÞ—¸ôv¹y»ø¼ÒÓõín–t®ù÷Ñ—ùØUùýôo–óùž—U.t÷ÛóuòÒÕÔ>×zúßoÙž-˜LÙÿuñ×?ñwXRÚYÝûZôó²<Òÿ:îýumõQ\ÛTxÿÞõp¸u¿Ý›–l,ÜñkWzV×[_Þôîwºñ5ùÔüêvvòõöTxQÜ¿[t³]µò×ðYÍûq5¼^¶-ýQYVœÒ{Þùø:{³Z´PP¾ñ|^[î^wŒTÔÒ™ÙÞþõôµZñÝQœë›´ùúï÷ÔX>”ßõº~î÷õtë¯?ñy{ýì¶Ö^›ßRþ?:Qíúy²ÿ»_môXúßíòÙ|ÖÐÖ~×zøy]ò¿r¾Ü6pX—k\ÜÞÚ–RŽ÷Îþ™vû¿÷¿;ßúöú×÷öuÜWùLPœYÝ™ö´uð·¸iÙõ/_u{îz¾˜ü’zÓYþÚúvÞðøü©üÖ«¹s”9æZþšÚÛYÞû™öõ÷zúp°þuÙÝî_|ØšÑ|™wÞÜùp¹5õvÔs»ø¹kúZÿÑÔÚÓ~ûXx¯°¹tð|Óõ]¼r>þü×NÕÓY×{ô±Vl³:ÿY1úþš/sÖ2YTËüœÕ½öxrsôp¶Wö•|ôyùp{ß®O“–PÛôuò°nžÞ˜Þ9^7[îùøøWÑÑZYÔ\µs2¹·,ÞZXÞ~ùžz¯û[óPN˜ÒÙýÚ´í{3²ôYšz÷ÝÛóvz¶÷ΖØÔÓØœ|4ðxnµ»¿žpûUî|oðZQòQŸÝý÷ýío¸,²Vy´÷ýÿv°ø«ÓV™TÔع{û÷îœ.Ü7Uþü¹ýußy™OÖÐ×Ôšþ]·±tÙ.\”;TøöÕy{ùoIÔÿÔÔ¹Þûtñ6¶óÞœ=ö¹Zùp°ñÌZyÏ÷Оó^sq:².Ö{·¹}ÛkUòtOz×Rÿ[œ÷{³²ý±/ÖÝ:Ÿ·ýî~y+PÕ×ÝzPÛî^ïùt·2Ù¸ß8{xpzrÌÙ™R^O¸sY~o~³:—z]í5]oKuYÍ^ÝÓ2Zý4t¹5Þzû]]ºvòÛr×]ÜÍÜÔvûts¹m4³ô¿>øß³óX-[RþÏ[›]yúóoÞkô¿µš~ú]íqÔoüXNXŸVuõu.ô1òq¹½|ôßmzsYN÷X•Ø¸´_ìó5öw™}ÜÛtÜøùõÞÊ›R˜Ð½÷Zoy8õ1™Z?|Zøï•øïÍ[WÏ|Ôz¹wxñ´1¸_~ß3|\pòZi“^Ú=×Ý÷šrîoø2·8û\úóÛrð_íßTœÏ<ÖUôù5ízîz¯Ý6Y|ºxXlŸÎúÌÔ–S4üû·úmµöÜ]YÿÚt~¸ÝÌÐÒ•Mø›òÿòô5»—<y]Yzvû1SzÑôÑ}üëv÷±+º¹ž¯Úr^sýøðÛZZÓr_ð\uò¹ï}´¼špžÖïwRnÛÑ]×VóX~o¼/ÝsùVx·[ÿxöûÜ™˜Ê}|к½z«ßðµ0ùYõß^úù¼ôyÎ[UÏ_Ñ;XÞ²|o¹7þ|øZ]¼÷™øîÊYÌ^ÔX/Óðîú±4¿ñÞ¼³ûþ3üvïÑß]‘u•ÔoXqóÿï8¶X6þzWvñ¾ØõS][Š?–XvÕøò}·:¾Þ\~öPþp½\pÓ\ÑR‘ø;Wìï9µt»ù;z|÷yž÷tQyÊ×tSúûüñr/ßr7¼4uXyðÿ´{uÎVÞU÷þ3süêô¶xº<úšó¹ù±ÕÕÔO{”Vñþ4myö7¶ÙXÜtÚ»úþ_pÑV’Ët”\›øq÷õµ8tºU{¸Ö÷û™³ÞÑÕUÎ[ù×ññyròø<»¾_þ¾uZ3ïKû\ŽÛ¿ÔóÚñ²u²ö¼¹[÷zZúïÞ÷~ž’ZUÖÔðuºoÿ+nŸ>÷¼û^üò]ôÓ?ÒÎÙÞÖtº7ö3ï~~¶>ÿUuu×óYÕNYÕÒðüyµx¬2ž˜[ðüSvz»wÝ”šÐÑ|ÔÖ¹ë;~nl´<}½ô|þ½°~÷LÖxÑZô*²öl¬zúµö}mß{ôZVÑZÝ›\hº~îÚ5µÚV]xpTÕ¾9XËzWÞ]ôþ5ï\˜Öl˜ÖûþÝ^ÒšÕÓü~þñtuï<ŸñoÙÿðužl~ÙRøþ÷~œ«µszy¹÷óÚw]ômûŽZÞzQüs¸u>õò¸Ý:XغµßÛØÉYÛú}pý{¬ö~óVtÿÓõó›YÌvÓ[óSpð¼r5½{ÿ»ÿXøõÝv˜\SV—X×ì¿þ¯~õ)»R<súsývYŸÑÖÛW^ökÝòªx³>{Ùýu½¶ºÕÚÎ6ÕÎÝìÜùøl·>/—úwXYpÞöÿ^¿ÌÚý¸qõ|í®ö=Þ´÷ÒùwÚ´¹WÝÍTÓtžÿÿðôu3Ö±{ѯY|sÙ~šÍW}Ö{þ1µüéôþ:øº\VîyÞwÙÇ8~Z½ûøq_t¶´~™¶öÔ}yz¸œ[MÚÕYô|ò»þñîÝ:ܽݿWûsÔÞÊ{Ò\Þ<÷ïn»yw¾¾^™ôýõvØ’P|Rpü·,¹sî5oÚut¼]òZn÷—þYÒ]Ó4ù,t2÷øX¬Z±¿ßúVÛZÎ~üÙôí\v´p9—ørÔÞzÞö~VÜÍS›“w\ÿxò±t7µóo[[ôõu{Í×\Òpõñø1³õ4š>pÝt}òõZ˜Õ’ÞÓ}ôžq®øöv°õWö<ÿxürú‘ÔŽSY•½¿·õvsôowÛ<p_Xz¶ÜøZÝÍÌÔUÚ¿œûõuñ¶í4¿wq‘ñ±üøyžÏ×ÕÖ}ýØ5uñnÜ®:Ýó?ÖuûÜùúX”ŠÚÙÓ>X}¸w·v}®~V¯Zzsþu—yÕOVY—xÞøs5ô20îÝ®]XöôÞy¿¿ÑÎYÜwTÛðk8ù7í²¼øwq]×mt•NšÿZTRuu·Vq³tù–u3›Wð6^zVÜÓ•Ø×w~=÷yôïµÙt-Ùõ|õ´W–ÙÒþÏ~?w>v÷thóžqr—ø±ÚùòÛOýÚRÞüøxð»÷p°\·4×vnV=óS”VßPžó×ñ8tx2½ÔY®Ý—÷úþ´ÕPÒZÓÙÿÞ«ùóþkž¾lxWôöœv¶’UÒþÏý]ý{°u°ñ\Ÿüð^Zï{™löØ[RÝ[¿|iüt-³Rw¾õ•wµxzr\Q™›ÓÚ^~úôk=íoÞÛ=¯Ö¼vv½2uÞÌØWÖVÚ3póú0¯T›õøÙ«Û÷ÿõÑÑÛUOø™Ûqô5ù2ZšõZÒñ9Ú2{›ÒÖYÒTø{qm÷2²Uuu–wrñv»ÒÝþ™Rßõôpjúqî™}:üÕôlß´sÖÕ\Ñ‘|tô÷=ïÝ=˜×·»Ö²pûyu–ÒRûT’|ñon½q–Wó|º½ìõ;{MÖÜzÔmlûý±yÓÚ^¶õvzs¿tUØÏ÷Û{ízöÜœšÎú·ølÝv´T”šPLßé˜×êü1ÔØ6–Ô_öoò{y³U—YÓºøïÔ¯ñxÖ½5¿O<òéñùü´\[ÿÓ·q2öíüUœu¾RZ®¦ùÿ{¸Ø×zSüsòØ^nZVØ<»ÌézÛü½ÙЙYÎÜrs–|÷8TÔ7Y‘ojx]|rRÚœ˜}÷oÜßôñ]ö”Õ(æ^yzúºÕZ·
>nyt{Z°{R¬OñrlðÕ÷vZÛ^¾—ðõxþ;|ûÕó©éþZ6?ÚØÑ]œ¼ñs]vß<ûyZ’×(æwZ}uÝS”~\¶uûY¿Ú6ÜULœæqüÙ]ùZÐ\Y÷ÿúû–Yö¾‘íeùZµÿÖÑWûTÿöó·þÜô3üž³¦ëÜøtuÿÔ—öX½´õqxÜ[r¹^Î+)òØûu›ÒZßܼìÿX[ý9}P:æôù\÷þS—˜×}\vô{Ò÷ñÕø©lóÒ÷¼]ÏÜÔý|ßwðv—Ý=¯Ø™êhí™ørÖÔTzz—róu¶{t6‘–óiêÜüòÜY_z˜útòvØÕµ9—Ug®výx¿ÝÏR—ÿÛ|ß±ø^Ò=ÿ\³ærx_¸^WWøy}³p·ÕÝw¹Õógîöß½˜NÔTøwØö²½ŸZœøYRõ(èsŸŸþXOSÓy}|}59}|XxhçùõÜüýÖÍWû}¶žô¶¾óîis_»Þ~ÐPÏxyû¿ùû;\?TÑsj¨nØøô^šÎÔþuº}úôö·_”½ãö5~|tÖTÒw_ô\øö²Ùwëëy¼uù[ÓÖ»sûyv÷¹>W™xcûwÿwÓV»ÝwØ^¶_º›ÿÕÙ>)jzÝØ|¿SÑÎ||wW÷Ùžý»_7km÷]Þ|ýTL÷_t¾™õwXº}R˜¶ç±qþ_õýØÎTÜvú=ÿù·:¹üV”îåê|xùöyÚ›ýó;8xû·t¹þRWêg·ü»×ðÑÎÛœx¸ZÿþŸxÝØXö%òÿÿÞ\SOÕþxvš{þ·<òš^êèì~Ûó|÷‘ÐTºôöÛz|¸ùß™Méh±šzÿ}]N\4yZ}}·¿~”Îx)kö¹û{úÜLMy÷ýüwûr·\Øõd¬:ßúõzUŽÔ˜÷·ßùþ÷5úÚVî(ëÖsü7ÜX̹û{Þ{ÞtöZžLÕéí5ºþú|^ÍßóUü¾\·¹~PÏò¯îtü÷ùøO‘Õövþz»xî¼üÚ÷çðsÛñxöXQ¼8ô¸ü¸ö÷w™Q˜ðåô÷}÷{žØR~ú<÷›øù¶]TŒÛªòtöÞyyŽÎ’þ^ýß}yù˜MQ÷ïðñv¼úÔLšxyûÞóº3‘Òßnîrtùþ5ýwÝöÿûüïx·˜óë®ðõz´ñ×URŸùuóÿºôí8ÔÒ÷4ésÞ¶û|yÐÐ<·ÿøzryšOÚ:ì.û~yú\ÑŠT“t_þÝýö·ÙSý-¯|q¼õù×ÒÎÖ:½ùûÿ2³zÖÒßïñp¶üðõ^‹üYz¹»?ñðóÕW_ôíu·~öñüMÖšßù›xszñ•òñ¯\xï½4—QÏ}Ûÿ_ýým¸>ÓÖþò¯ßöýï:”KŸQ˜~ÝYwvôz“˜Zïv{½ñoÿÞÕÏüÛœßïùoþÿR;¯ï;ºk·µõRÑ?þÙýùöð,|ÙÚþòî4½6íòYÑSü›<ÙÝsòv½ŸQØ°lþY-rüžÑÎÖÛYSyû¼qݘ—tðŸ\÷nüWXËžWVÖ{ŸîÝúŸxõ=ôtösu›ØÎ9ØT×oûµ¶wÖxëÝwý,.X\ÕÙ”Sµóøu³_¼ù¼ììÛpò3ÚW_”VÜoÖìõX¾Ù^rñ¸Ýq.½WÖÚÑØß±÷û·û¼kšÛqó|[VÝÌÏ÷–û|ôw”˜9ñ÷×µqô»VÝ–”—×Úvùsûµ}[ªñxÚ,«[|ZöœÌظùzóðí9×nkœ±ï{ÝZüþM•[ý\þòó—®ùÖúp¶˜ÓúXLMÙÙ\Òtò_Ú¬y—ú8süVü”J~Ôý]´pØý.yòqoŸºúýZWÖ¾ÝÓo¶2÷^í¯øôvpô<ùyÙÔ™~Pyyôtÿ²ùqò_ïÛoYz™TÖ[•Xß~0Õy¸{rþôów•x®ŒØ×ÚÕTÙ>ÝxY;œïõkqÖÝÒÜ××_ÿ_ø1ýûÿpìð?o?¹{ÜU}ŒÿYõrSîó[¸°îúuzmÿšv¼Õ‘÷Óü±šõßs¹Ú÷ñö<nÝóÔÛÚÚS–^»™|ø_¯\Óÿ«þú÷s–:úYÏÒÝþŽvùþÜú:»Úôñrþt¼°7LÎú\^üÞø~Ú÷Øœ³òrßï¾ñ}|ÚT_˜ß²ýwþo½ú»êñr}ðø{¿Wñ•Nð¶};øšîßý´k|õöün•üZÕ×z‘mžØ|÷þz=róßòô“ýqRÌS´\Ü“øùû–ùW´oœw™ô}XÌšzW¹sÖ|¯_{[ï6oµð||wÞ[”“ßÖø÷•øúº;^·è´î{øýóXÚÿP÷^xó¸œzjypðm3–ðúX÷óÓ}/ÙUóµô3\öº[ÿÔOZ•YÙÛqœÙ_ººßïôrWïùØÖRÔÑßy™úÜz¾Üò7nøn·ß·×ÚØXÑÙpñ»—w÷2{í²svúøqÕVÚÚœÖtu½žþ\«ßòo´Ýz¹¾SÖQóÝ^öÛ8]~÷î®tZùm‘U¸‘’üûÜy{ž]ß^õ±lñ¶üý7WÕÍÏûߺVöt÷TÒ¬örª×7zt¿ŽYöÑÖtsÝz»ßõ¼x¨ëÙþr_ÛWѺzÿ¾1¶Ûðöðîß/zYÏýüMÒóÛsÞô\¸tñ±½øûZûRÖRÙŸZ³3œW^øtkû|µ›YÙÑØ•_´üwœÝX»,*÷^ôv:üQ–_SÚðyžlõ^òìj¿úúoÜÔ—¼RZ™þ|óù\“3ûùæü|·ú=TSsє۸yõzúv®õ¶óU;zÑÕÙÖÚ×òÞ2»˜Þòñî~ùúÞP“YZ~Ùö4ž>]Øñgúw—kØ[_ÜOœ\¸/Vþ^ülo¼ïœßn[YÑû’Ÿôwœ²~—}qsôpy~ú8ÝPÕ^Ó9¶Ü-¼þÝónëyò³üñÜYÚÛØ}÷û5šö
s¬qý>ÿñ•™ÖÕWÖYýZñü–}2ôøÜyv“ZÝÑÕ[И¬y<›[Zñq{l´—öõÚTÖ5ZÑqsžnV³Õxêv¶wq=“ú»Ð×s^vs³×÷oqóÝyù}”W™UÛ|y¸0ºØvôïïüÝ^ê”Í>SÙ”Þ~¹µ\™Ùô®øð={úùÐÓ·ÍÒu˜þ5¼óRûõêý?w|¶ÕTÖÖ“ZÚöw]—ÖnôòûuÝôtT’ÞÒþÖ°.™yy_íï·ú³üs×x”]˜Rî®ü2Ø[îúíuüÿðrŒ•ø^ZŸöt¹]Rryoºü¼³ÜÜQ~ÕÓ~×ôíZ•U-¼2xw×t6RþÑ[™^»Uð«YÕ·÷uî{ÿÿõ˜Y”ÕyŠðü¾ø¸÷Îó÷0÷Ú¶øuP\RþøóÙô+X/uóß°þ^½y‘ÛÜÎw[÷}±U[³|q´®Óo\¼ßUXUXôSï¸ù¼W3¸ïùÞtøþÐVwŠ:ÿ]ó»?Ô¹ü8ìuûu^™ÌÚ[þY[èò¹vLõ³6ðúû^÷Z–W‘^´{±´÷œÕvùçØuµ×´ÛXÓ~Ó1þÚn°}UØv:tp›û{ÑÓU˜Ðݽì=÷xÖîxs¶û>ÞpŽWÝPÞÞx´mŸôÖ]ë\pðšsÞ;ÑOt˜ÕûÜé¯8»X{¸§üxúqýÑÝQx¼zð¯7Ý•¹movÞïÚ~ŒÖ}Oÿþ×ô¬Z™—wwþßôú–ßYÌÓÕÒ}»ížx›ô÷t²ÝwÿíÐNÿP>\òñ»ü3Ùü'|{øo»ÝØØÕX2³ûð»=növpm×.ÿ[zÎ<×}œ\ò÷¸ž\íõ0^v¸ÿÚLÖÛÎ}™^t³Ý˜quôú߶ü×X‰YÕ‹4ÕYô¼}ÔÞò]»ýy_{XÑ’‘UÚ^ï¯ýø±ïóþùïúÿÞÔ]x›6§}p¶xò³î÷v}ôpÞÒß3Tú6õó}–y0úúp~–n^Ö[Øt—wøë^Ò{8õñvWõ¸×Z”Ñ—ßz‘òï™7Xÿn¶Ý{ØØ_ÍÞSûÙ°|·þT“ïýóŸÝwÙXZN~Ôx}¶nÝ_>õ½p¬öUtïÙRÔ8×y–7©ºÿ·w÷ìr[qøÞx’Ø{ÎsŸuî·9Ø^puí÷_pqU>“y–Rt¼ps»tØwvryßvÛúUŠù–Õýð1ûºØþÝóòw÷–ýÖÍÝÖþþ°4špßöm|^òÑÜÖÐV×|Ÿ7p»1^÷÷ùíÛ_ñœÔÍÚÿÐý4ú0^ÖqrïûnÜ™úÎ~—]W?¬nZ»[5ÞõñŸ³ÛZÐQ>Nؽ÷òÿ1¾Wtõówv߸ÿV_QvØyh²:Ù¼s:u¯»XýõÔÍÿÖRôšo}ôø¾öïýywUù›ÌÔ–|Û4©t4õQ^°ò÷yÿvýXŽÞÛR:Óoò¼,šÙprùôõ;ûßzNšÜÿ·zx°ìT]øq]½sÜ{ÒÓ}ŽZûÖ¯wúŸ<øÞul\ž³ÖÖU”QŽ]–6ù_3ÙÛûÝôt_w›ÿ[PÕ×ßÔo/›1}÷üvnøúu|7ŒV›Tzüòﺴyzöul0üö~ÞÑÙpY´öq²2ž¸öùjôØzïÖ}ÖÞÓ™öþ·n¾³Vúß/w›ßnPÑZ\:ý[o·z¾SvøµyþÞÿ>ËV^Sýw¹nŸsÙsöÝ÷ž÷XþYÏ^Óyòî¼o·=ð±¯×÷÷ÞÙ•ÒÝVzØøëšt>]ýuuq˜]xüMMZ˜Nº{±q8»ÜöùrìùÒ©úSY•[ž\Øï-ºù4úÛlñýÝuóÖRú™Õûðî´]ìí¿Twüÿ–ÓÖWUŸúüoŸº5»Wèsûù{ÝÛÖÚÕtù쯺¶ünù{øvß^[‘N^þS°é_šWµîß:;\õýÒXÑYÒ{˜yí±]·öïxŸÝ|ñ˜ÑØ]ØÜ>ò«^Ùòþxî¹öZ¶[ÑÙÕÕXw¾%µxõ÷ðvµøûZWXØ÷ýuî´^6·Õ¯rµýÞú\ÐYY“øÞ¬³Ù9^sõΚ®ÖNVW”ZüÞútô”l}ün=×mÒÜÙÓwÞwn°Û<ò_nuµßrøþÔ‘[ý×°wÿé8œ²õt¶¬÷ÕqúV]TSò½Ü*Þ\°¯ûôtþ]µßÑTUvÚÜî5¾ÙW³Þ½wÿÔ{yKQÙSÛß{{·ØÛ4û8úõý[ÍUÕ’Ùu|ø˜>n\²®÷Úò¶ÒÏñÖÙ½uôkµŸØ¯sþqëÚvò>Ò\\Öýqqs´5Õôî[t«~ÿy<֜ӟxtt}ÚÕò6ðsþz_”ËœÑÚ[»¶tù“]þzüpùYºÓÎÍÖS¹¸ï»Wxð;²qüzž—O]Ùü=pkô×suùu®s_ö›–LÑßû~ðoU–óv¼´íºZß?ÍÓ›ºÜþxìô3ps[j~ø|ÞSTÔ·—üxí½RÝ\°tûîð]_øœOØv~Yq0½ÑìêŸ÷wöZ›xÙÌÝ°û—~uõÕÝn±|t6”Vø¼Tõò×ü´1ØhëÝîù¶W÷ýùOõsw•¹tµWõìï]ò_Z—µÞPT¶ø¿ÕûrŽüoqu¼û×ÚýØNÞv»ÔtüP«î²øøõÙózÔ·ïô~|¨òíh4:x÷Q~»œQ_qøY›òÜÑMólñÛó—^OœÖ™ÜxøÏtŸVÑ™rëYûß{ÑÖßy¶}Û¶6nhmùûº:VœüyYqpt¸oþ÷ѳçì³Ûõ^˜Y_Þ{Øwº^߶VSìçu~×ÿX¿Ý?Þöü\|9”M¶å¬úXÞ]R™Ú|_øwÖ9ŸÙMÛ)g;ß\ÙÕ”[»Ù¶ûû~ý;‘”íäkÛzÖÜV÷^¼øz~_Þ‘ÐùeëÚ?]Õ”šù]ºú_ùz¹X\féuþßyÚ–›ß{½zøwº÷šÑkçsß;½ûSYÞ}úþ|¹txÍìæ±;Ú²|W™›|x½Üù{y»Ísîìu•z>ÔÕÚÛ|]üž{þRÏZ®§›ûYý›ÖTZœ]ûýwyÏ–jiõÞ[ø»UþwÞú|óõ~+*ìùÙû¼^Ù]úüµûuòôôÜrã·yþ[sÚ_}þ~µ~¾úö¶³òëÞ]\•×^ÿ—ø=|tÓOœqgyÞ|Õ×ýZzßv]¼uÛ“s'ñ·ÕÝ\Ö^ÙÛó|9üýô·ORû(§ÞßþÖ_Û¸òÝ=ysÿÎ?jk8úUÞ}W“|Ÿv½zß2yNÓëêuÿÙ~ÝWšT›0÷Øy²wY’”òfñ¼þ:ZÖÞxsÝtøõø˜Éóh,ß]œ]ÞZT˜xtü½4´ÑüdúÙ_z™×ÜV|´ü¾Þv¶Ëõòì/TÝÿÞXÕØšñ¼}÷úÔìç~wÖ{Ü—[š4õý}x²Q”°çrýZ_x{’\×2øxþÞí>Òõin»ÜšûÛÚÓš}´ûþzuz’w,æ^??^]TÕ}ó?ûÜtú“ÏÝn©ñÙWûV_Ñ¿ý¹ÿÛt¼×MWjînßß¼]ÒÕZó:ö}r¸üPì¥pù\Üþ|›ØT´m<xµ·ÙUïh©\œÙúZþRšöwúû]ù^–®éÝÔ×ZžÔ’ôrÚ÷¾Û×T3çt´XSõ\Ú~•73wþzúÞUY3ª)úÖÜXûÙWšûvt<u¾ÔPÕòêq´W×ÝZZ’Swß8½?™WÓÛðêqµVý{ØT—ò¼ì»~¯ÔPýnîk¸{VwšûÚwör¸õº˜Qþvl꼞ÚÝ[]T}ú5ßu|ÒÎú5îðý×]™ßSSÿùuÚ¸O×¾r®ÖúÕ<tÒ•Ù¸°ôýïÔX¼çvr<÷Ý÷ß=ØUðx´ò]Yóm*x9Ü^óZÜ’øquµt¼^ÐØn«rsSuýzÞW–u9o½4_ÜOöô¨n~Z<¸ÿTÒñö4ðüÚØúhív¼½\þûÝO¸µtoØðÌU¸«rvÞœ–óÙÚ•Ù÷±÷\µSÖÓròïÿ[þ^½’Ó»ßðµz¿ÒPkøð»Ø^þ_{Ž·ûxw8ö×ÿ2nôúÜZñyÙS÷úñör~•×uë2ïÛp}uòØÒ=ü±t´®Z\Ôt§ÝñüüüvŸ[Qøú»u¼ÔOù8ðp¿ÔxøÖSOšÞô¿zþ“Ñž+}³Ý÷UuîTÏÿÚöût«W¼?øêðoÜó½ïy¾Óž5²/µvZ|.ínýzº÷}wÎÛÛùž²|ØMôvÞÝÙÔÝ»ORÍÒž]¸ð›Úþ°nÿÚ6}ÝØÍV5¿÷ôôWŸn°q_wœßöùO×Ø}°n×\¹êõr|³[õòßSÑ\ú0«]örê¶ô5ÜþózÕOÚ×;Þmñ¿ñ²óõö^Xú¶UÏšTþq²9Ýqöl±XÞúýyÕÏÓÔß8ñïž}[î±¹öSpÿT
Òøþ·uüÛùnvý<ûPôÕ‹ÖÓ]Úrvº~žrµnzܾÿ¼ÿÕRÔ¸mòö\÷mîwøñU¸q^Ó••–ø0ð6yø¹ïqïúü¼ÿÚ’ÍR;Ür±øYq¯y÷XÚÿZ|TÊ“×:^ñs^Ýýí2º>»S÷[>MMÔØ~t®ú¾\ñïxð?ÕÛrZYŽ”•ü·ðìõÛ·sîõx}Øýt_“QUðª{û|±²r²_œÙ÷ÿÙÐNؼ+s{ß·v±põØÚÿýQŽ“Õ|¶ñ1¸Þ]lqºßÓyUÝMJZ™›3w:]^yïx¶ZþSµ¹ÔÍX’|¸íñüýúqñïÝ][¶×x[O4Þtê7ý»õõo²[Üz^[•NÕ^°qïø9Yô«ýóþ›ÿyØËYÙ~¼©ò[;þqôñÿÚV:yœÊØ“\4ëz[søpµZ\ßõÛOTÕ<õkö²yv®÷þ|™ÕvýÔŒyükq½×uwó¶žÛZÞ=^MØعn®yüÞû/w¹>Ò|yþ“šÊ×òÞpjý[õ4ó:ôÞSrÞþÒÔÑ–v»ou¶˜¿o{òßÝØŸñÚΑܻp¬tùÚúoxµ˜þXØñ׌
ß^wu¨sT¾rö{ºzÔÚ1ŸÑÎU—ÝômôuXt²÷˜þXÛó—ÕK^Õ{öí5žýŸør¹ßÚùûØKØNù_í±p5Ö´nxs›óYúuÞÏW~·)¬t{Zòv0¸yš|øÐ_Õûºlìߘžrßñ»Û~ÞxYÍ
Ô–öøê:þÖv~;´ÿ–Zô]NÏQÐZ²x°r;õx¿ùžÛòZJ^Ówûmí·Z÷4¶÷õ6\tòÛTϼZuol¹¾ðòþyµ^øu’Ñ×]s1´oZÙÿó^¸|º™ûÙMÊ–×|vr÷÷6´õÜúx~þXR”Þöøk|´Yþ/øïšôõ}û—ÑN½ü²ö¯>^×ðp5û~ºß÷ÙNŒÒwñrsØ}wµòšö[øùKÕÕù9öqtÝÐú-·?øõ_v™”Ìvù¸q®ÙØWpð4ß5ß}ûWÍÌÙü8÷sw˜Ó±rxïݺûöÛØŒÏtz¹1\W}÷²pô}÷½ö×RÍ\ýô³³qÒ6sp®ÙótûPÎYÙøò6¯XÜ.úsú5xÿºWNÏü™|¸q¹”Ð}qruœú]}þÍ‹Ø~ün½ÝSÞ3ðs¼rWøqOØغwön{˜Ö¶noux³×í]˜?96øoô{Nz1tµ|}yý™ÎÑûzõóךßê62ûYxúÊVÕ~y¹û´ÖN]o¿ô´}ýQLÎõ¿X{²ÚŽZõðt÷üX¸·Ê}y;¸l}Õ×wplsú?.ÛÖVWxµôs³ßÚÙlèóõvýv´8PQ3x2²ïÿ][ðlìr¿t}yø‘½6ýô²ÎV~ò³;vX[üþÍRx{2Ÿ^ÚwíñøÚ¼ŸûY×þuv½qþPN¯m²x²TXvùRÓÜtw^õÑÐØuªuÞÜÜ×TÍüq>~9?LR®.÷ô·RüxºÒÕ_3þ°tw”Ðøhªzû}{ÛnÛS[oü®»´:Ïòökz±Ùú¿_”“x÷8³ZôÓ•¼iíxÜVùüWÑXx²ùñ”Rùínqõ•~Ÿø–PÑ|³Ÿx¾ULT³n÷»ÖØUùZŒÑÝy}ßuÜLÐ9ì.=øÝxôÐüyòyl¶ÕÙ»ël긹^õµÞUÖst®û±uÒ[ëöín?ÜóÔÕ\xòüö~Ðœlêx´”V5ÞSW—ÞºôýßšÎYúmïZÞÙYS”w÷XúýN”üip³ùÔ¾vÖ“Ü:rzt¶ÒV:rìí²ZÞ^wVØ]vöŸúÕ“½él÷ÛùÕ\{›”-öyv¶ÝQgñíóÝü¸öºV×ö2ùöóý×Þ´iì/Þ¸Þ_4ÕRÿß°üüµØÌlí³½xTÖxÛUõ]šxZ˜ÛkúòÞšTÜÔÓp\y÷ÑXðëóòþXÝóš–;ÿnþ?µÛÖYjæôóÙ:[µÛY”w÷¸÷V\õíi-|Úó™Û•Ûw]yúÛ[Ù¯ëð0ØÖ{ŸöPÒxóšþý·Ò—úml+|ŸS÷›}Ïüùß{]ß×™¾iéoýÛ[z]VÔ”|ùœÙŸzN–s®jv}ÙØS“Ûø^Ùý¹ÞUר©l\[²~ùÛÛxt»zõóž÷ë©)ñ]œü±–×ßÛ5^Y6ºßÔZëìjxúÒvÞÖÙÚ{Õœ}¸[×;óíhñUÝ>šÖQÿÛÙØ\½ÞÔ°ììöž[ßÕÍÕœÚZÚøß9”úçrù[Ý_\OüY÷—ÿ¼puš]u)jóÝvÝ9¿‘_ûÙö_¹q³ú\¬díú\Ù½Y’Úþ[Y{¹÷þ˜\vì©6ü_Ò—ÑûQWÛwùž^¸~ñiíøv¾”ÓÛ–YRÚW]¶ºÕ]ùöñîºzr–ÙÚ]™^Ø“íóñµþøgšòñyÞZ¿øš_üúsýžµ¶õrr|÷*ûμßþÜÖØ›rÖÿóX[i·×ðð»ÞÒ~þ˜ûÿÙÖrÚöxó߯”uøÙ¾UQýõUµSºÞYýø_^õ‘Ôn™QÞÚ]ØW_¼|÷Ú}/Ñu¾Ú¯”~öxSõq¹×ë¹^z1\5rúÿù®÷[ó|5wùùñxX¾ñw\¶vþšøÿz’qöÚ[õyÛ.ØÑo¿¿ûßÿñ¾Z°zðîr¿q¹>ý{ìp¼[åÚXt´ÕóðÛy±üÞvô™|;ßS×_•ÕØÙ{Ù”ùÙÌßq”ÓÞ”•ýZÔ~Ø÷ÕÏ|Þט¸1X×s—ßY¹¸ùõ\ýyÓtnÙrï}ûÞ0ßò®ò¯¸n~úùõ{^ÿÒû´|Z¿=r³¹øx_Ösøø÷³Ý’p›tûö{Z÷U˜ºu[÷kXŸÛÜWÚ^¹™[ûR×UPý»Z°’×Y—þù}TnºÖÕÙ·TV^,Þ—þºþ|øþóîym™±´×µêÞÚªx^ÿû|×®íòøì¸Ýùøþx®Òð·UÜÝö_¶5òß›¾s×ÐÞï}t¸ÙØÖw—XÚµXzú–YŸÌÿtyQÓ?ôÓÔUØ÷º{Ô»ÏzòóŒ÷|8¸Õ6q½qû{ómµšìúTtø]4ß¿òÜžgÞÜpïùÕýlw˜tÝW·*¿ŸÐkR}Õû—wÞ]ë\RZ7°,[}ú~üúzÝÜò°|·[œîóNmŸY9n¸yÖÿxÖ]XôúÚRþVØÔœ]žÒQ˜Õô[\›[´óÓý]ºÜPþTP_ï²ï›\|ò×™Õôl5ú:ö·rü{ñ´{zsÝs´ôôoq:û?ÙÛÛùm]t1ß[÷w¹÷[>=×VÚõéÝ×k\Ý»ØÓüÓ¾Ù>ÿ³}Xº³Øö~ÔÕÏ[ÜÚó|z¾”ýtt‘”ù^LÏrôü9ttù:ÿ_t°øÕ1îç¸ö1p~×ëZßþZ<~ûúñð|¸óšøÒ—=Ü]ÿ—ZëÛŸù[7^¹Û•˜Ó¼p~þôì3ÞUÒóºQÕÝÔZ_þ›úÑ”¬ü×W»ÕPþxÍž6û7zúsWnÙr¶YÚžyò~z¸mxœþûn˜Í+îíìmvtó_x7ôù~8·õøW¾÷ùpüØþrÙ{ž™ðØ°w˜^ÕsïM;YϹŒ2W;Z|ÝÚžý~Þ>þ]ßßö\]ö{zÝøY·]Û}Ù²ÿ˜¶Üÿý{^
\ No newline at end of file diff --git a/tests/gatt_test.py b/tests/gatt_test.py index 5df6e08..0652197 100644 --- a/tests/gatt_test.py +++ b/tests/gatt_test.py @@ -22,11 +22,14 @@ import struct import pytest from bumble.controller import Controller +from bumble.gatt_client import CharacteristicProxy +from bumble.gatt_server import Server from bumble.link import LocalLink from bumble.device import Device, Peer from bumble.host import Host from bumble.gatt import ( GATT_BATTERY_LEVEL_CHARACTERISTIC, + GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR, CharacteristicAdapter, DelegatedCharacteristicAdapter, PackedCharacteristicAdapter, @@ -34,16 +37,18 @@ from bumble.gatt import ( UTF8CharacteristicAdapter, Service, Characteristic, - CharacteristicValue + CharacteristicValue, + Descriptor, ) from bumble.transport import AsyncPipeSink from bumble.core import UUID from bumble.att import ( + Attribute, ATT_EXCHANGE_MTU_REQUEST, ATT_ATTRIBUTE_NOT_FOUND_ERROR, ATT_PDU, ATT_Error_Response, - ATT_Read_By_Group_Type_Request + ATT_Read_By_Group_Type_Request, ) @@ -53,37 +58,37 @@ def basic_check(x): parsed = ATT_PDU.from_bytes(pdu) x_str = str(x) parsed_str = str(parsed) - assert(x_str == parsed_str) + assert x_str == parsed_str # ----------------------------------------------------------------------------- def test_UUID(): u = UUID.from_16_bits(0x7788) - assert(str(u) == 'UUID-16:7788') + assert str(u) == 'UUID-16:7788' u = UUID.from_32_bits(0x11223344) - assert(str(u) == 'UUID-32:11223344') + assert str(u) == 'UUID-32:11223344' u = UUID('61A3512C-09BE-4DDC-A6A6-0B03667AAFC6') - assert(str(u) == '61A3512C-09BE-4DDC-A6A6-0B03667AAFC6') + assert str(u) == '61A3512C-09BE-4DDC-A6A6-0B03667AAFC6' v = UUID(str(u)) - assert(str(v) == '61A3512C-09BE-4DDC-A6A6-0B03667AAFC6') + assert str(v) == '61A3512C-09BE-4DDC-A6A6-0B03667AAFC6' w = UUID.from_bytes(v.to_bytes()) - assert(str(w) == '61A3512C-09BE-4DDC-A6A6-0B03667AAFC6') + assert str(w) == '61A3512C-09BE-4DDC-A6A6-0B03667AAFC6' u1 = UUID.from_16_bits(0x1234) - b1 = u1.to_bytes(force_128 = True) + b1 = u1.to_bytes(force_128=True) u2 = UUID.from_bytes(b1) - assert(u1 == u2) + assert u1 == u2 - u3 = UUID.from_16_bits(0x180a) - assert(str(u3) == 'UUID-16:180A (Device Information)') + u3 = UUID.from_16_bits(0x180A) + assert str(u3) == 'UUID-16:180A (Device Information)' # ----------------------------------------------------------------------------- def test_ATT_Error_Response(): pdu = ATT_Error_Response( - request_opcode_in_error = ATT_EXCHANGE_MTU_REQUEST, - attribute_handle_in_error = 0x0000, - error_code = ATT_ATTRIBUTE_NOT_FOUND_ERROR + request_opcode_in_error=ATT_EXCHANGE_MTU_REQUEST, + attribute_handle_in_error=0x0000, + error_code=ATT_ATTRIBUTE_NOT_FOUND_ERROR, ) basic_check(pdu) @@ -91,36 +96,223 @@ def test_ATT_Error_Response(): # ----------------------------------------------------------------------------- def test_ATT_Read_By_Group_Type_Request(): pdu = ATT_Read_By_Group_Type_Request( - starting_handle = 0x0001, - ending_handle = 0xFFFF, - attribute_group_type = UUID.from_16_bits(0x2800) + starting_handle=0x0001, + ending_handle=0xFFFF, + attribute_group_type=UUID.from_16_bits(0x2800), ) basic_check(pdu) # ----------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_characteristic_encoding(): + class Foo(Characteristic): + def encode_value(self, value): + return bytes([value]) + + def decode_value(self, value_bytes): + return value_bytes[0] + + c = Foo( + GATT_BATTERY_LEVEL_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + 123, + ) + x = c.read_value(None) + assert x == bytes([123]) + c.write_value(None, bytes([122])) + assert c.value == 122 + + class FooProxy(CharacteristicProxy): + def __init__(self, characteristic): + super().__init__( + characteristic.client, + characteristic.handle, + characteristic.end_group_handle, + characteristic.uuid, + characteristic.properties, + ) + + def encode_value(self, value): + return bytes([value]) + + def decode_value(self, value_bytes): + return value_bytes[0] + + [client, server] = LinkedDevices().devices[:2] + + characteristic = Characteristic( + 'FDB159DB-036C-49E3-B3DB-6325AC750806', + Characteristic.Properties.READ + | Characteristic.Properties.WRITE + | Characteristic.Properties.NOTIFY, + Characteristic.READABLE | Characteristic.WRITEABLE, + bytes([123]), + ) + + service = Service('3A657F47-D34F-46B3-B1EC-698E29B6B829', [characteristic]) + server.add_service(service) + + await client.power_on() + await server.power_on() + connection = await client.connect(server.random_address) + peer = Peer(connection) + + await peer.discover_services() + await peer.discover_characteristics() + c = peer.get_characteristics_by_uuid(characteristic.uuid) + assert len(c) == 1 + c = c[0] + cp = FooProxy(c) + + v = await cp.read_value() + assert v == 123 + await cp.write_value(124) + await async_barrier() + assert characteristic.value == bytes([124]) + + v = await cp.read_value() + assert v == 124 + await cp.write_value(125, with_response=True) + await async_barrier() + assert characteristic.value == bytes([125]) + + cd = DelegatedCharacteristicAdapter(c, encode=lambda x: bytes([x // 2])) + await cd.write_value(100, with_response=True) + await async_barrier() + assert characteristic.value == bytes([50]) + + last_change = None + + def on_change(value): + nonlocal last_change + last_change = value + + await c.subscribe(on_change) + await server.notify_subscribers(characteristic) + await async_barrier() + assert last_change == characteristic.value + last_change = None + + await server.notify_subscribers(characteristic, value=bytes([125])) + await async_barrier() + assert last_change == bytes([125]) + last_change = None + + await c.unsubscribe(on_change) + await server.notify_subscribers(characteristic) + await async_barrier() + assert last_change is None + + await cp.subscribe(on_change) + await server.notify_subscribers(characteristic) + await async_barrier() + assert last_change == characteristic.value[0] + last_change = None + + await server.notify_subscribers(characteristic, value=bytes([126])) + await async_barrier() + assert last_change == 126 + last_change = None + + await cp.unsubscribe(on_change) + await server.notify_subscribers(characteristic) + await async_barrier() + assert last_change is None + + cd = DelegatedCharacteristicAdapter(c, decode=lambda x: x[0]) + await cd.subscribe(on_change) + await server.notify_subscribers(characteristic) + await async_barrier() + assert last_change == characteristic.value[0] + last_change = None + + await cd.unsubscribe(on_change) + await server.notify_subscribers(characteristic) + await async_barrier() + assert last_change is None + + +# ----------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_attribute_getters(): + [client, server] = LinkedDevices().devices[:2] + + characteristic_uuid = UUID('FDB159DB-036C-49E3-B3DB-6325AC750806') + characteristic = Characteristic( + characteristic_uuid, + Characteristic.Properties.READ + | Characteristic.Properties.WRITE + | Characteristic.Properties.NOTIFY, + Characteristic.READABLE | Characteristic.WRITEABLE, + bytes([123]), + ) + + service_uuid = UUID('3A657F47-D34F-46B3-B1EC-698E29B6B829') + service = Service(service_uuid, [characteristic]) + server.add_service(service) + + service_attr = server.gatt_server.get_service_attribute(service_uuid) + assert service_attr + + ( + char_decl_attr, + char_value_attr, + ) = server.gatt_server.get_characteristic_attributes( + service_uuid, characteristic_uuid + ) + assert char_decl_attr and char_value_attr + + desc_attr = server.gatt_server.get_descriptor_attribute( + service_uuid, + characteristic_uuid, + GATT_CLIENT_CHARACTERISTIC_CONFIGURATION_DESCRIPTOR, + ) + assert desc_attr + + # assert all handles are in expected order + assert ( + service_attr.handle + < char_decl_attr.handle + < char_value_attr.handle + < desc_attr.handle + == service_attr.end_group_handle + ) + # assert characteristic declarations attribute is followed by characteristic value attribute + assert char_decl_attr.handle + 1 == char_value_attr.handle + + +# ----------------------------------------------------------------------------- def test_CharacteristicAdapter(): # Check that the CharacteristicAdapter base class is transparent v = bytes([1, 2, 3]) - c = Characteristic(GATT_BATTERY_LEVEL_CHARACTERISTIC, Characteristic.READ, Characteristic.READABLE, v) + c = Characteristic( + GATT_BATTERY_LEVEL_CHARACTERISTIC, + Characteristic.Properties.READ, + Characteristic.READABLE, + v, + ) a = CharacteristicAdapter(c) value = a.read_value(None) - assert(value == v) + assert value == v v = bytes([3, 4, 5]) a.write_value(None, v) - assert(c.value == v) + assert c.value == v # Simple delegated adapter - a = DelegatedCharacteristicAdapter(c, lambda x: bytes(reversed(x)), lambda x: bytes(reversed(x))) + a = DelegatedCharacteristicAdapter( + c, lambda x: bytes(reversed(x)), lambda x: bytes(reversed(x)) + ) value = a.read_value(None) - assert(value == bytes(reversed(v))) + assert value == bytes(reversed(v)) v = bytes([3, 4, 5]) a.write_value(None, v) - assert(a.value == bytes(reversed(v))) + assert a.value == bytes(reversed(v)) # Packed adapter with single element format v = 1234 @@ -129,10 +321,10 @@ def test_CharacteristicAdapter(): a = PackedCharacteristicAdapter(c, '>H') value = a.read_value(None) - assert(value == pv) + assert value == pv c.value = None a.write_value(None, pv) - assert(a.value == v) + assert a.value == v # Packed adapter with multi-element format v1 = 1234 @@ -142,10 +334,10 @@ def test_CharacteristicAdapter(): a = PackedCharacteristicAdapter(c, '>HH') value = a.read_value(None) - assert(value == pv) + assert value == pv c.value = None a.write_value(None, pv) - assert(a.value == (v1, v2)) + assert a.value == (v1, v2) # Mapped adapter v1 = 1234 @@ -156,10 +348,10 @@ def test_CharacteristicAdapter(): a = MappedCharacteristicAdapter(c, '>HH', ('v1', 'v2')) value = a.read_value(None) - assert(value == pv) + assert value == pv c.value = None a.write_value(None, pv) - assert(a.value == mapped) + assert a.value == mapped # UTF-8 adapter v = 'Hello π' @@ -168,10 +360,10 @@ def test_CharacteristicAdapter(): a = UTF8CharacteristicAdapter(c) value = a.read_value(None) - assert(value == ev) + assert value == ev c.value = None a.write_value(None, ev) - assert(a.value == v) + assert a.value == v # ----------------------------------------------------------------------------- @@ -179,37 +371,44 @@ def test_CharacteristicValue(): b = bytes([1, 2, 3]) c = CharacteristicValue(read=lambda _: b) x = c.read(None) - assert(x == b) + assert x == b result = [] - c = CharacteristicValue(write=lambda connection, value: result.append((connection, value))) + c = CharacteristicValue( + write=lambda connection, value: result.append((connection, value)) + ) z = object() c.write(z, b) - assert(result == [(z, b)]) + assert result == [(z, b)] # ----------------------------------------------------------------------------- -class TwoDevices: +class LinkedDevices: def __init__(self): - self.connections = [None, None] + self.connections = [None, None, None] self.link = LocalLink() self.controllers = [ - Controller('C1', link = self.link), - Controller('C2', link = self.link) + Controller('C1', link=self.link), + Controller('C2', link=self.link), + Controller('C3', link=self.link), ] self.devices = [ Device( - address = 'F0:F1:F2:F3:F4:F5', - host = Host(self.controllers[0], AsyncPipeSink(self.controllers[0])) + address='F0:F1:F2:F3:F4:F5', + host=Host(self.controllers[0], AsyncPipeSink(self.controllers[0])), ), Device( - address = 'F5:F4:F3:F2:F1:F0', - host = Host(self.controllers[1], AsyncPipeSink(self.controllers[1])) - ) + address='F1:F2:F3:F4:F5:F6', + host=Host(self.controllers[1], AsyncPipeSink(self.controllers[1])), + ), + Device( + address='F2:F3:F4:F5:F6:F7', + host=Host(self.controllers[2], AsyncPipeSink(self.controllers[2])), + ), ] - self.paired = [None, None] + self.paired = [None, None, None] # ----------------------------------------------------------------------------- @@ -222,12 +421,12 @@ async def async_barrier(): # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_read_write(): - [client, server] = TwoDevices().devices + [client, server] = LinkedDevices().devices[:2] characteristic1 = Characteristic( 'FDB159DB-036C-49E3-B3DB-6325AC750806', - Characteristic.READ | Characteristic.WRITE, - Characteristic.READABLE | Characteristic.WRITEABLE + Characteristic.Properties.READ | Characteristic.Properties.WRITE, + Characteristic.READABLE | Characteristic.WRITEABLE, ) def on_characteristic1_write(connection, value): @@ -243,17 +442,15 @@ async def test_read_write(): characteristic2 = Characteristic( '66DE9057-C848-4ACA-B993-D675644EBB85', - Characteristic.READ | Characteristic.WRITE, + Characteristic.Properties.READ | Characteristic.Properties.WRITE, Characteristic.READABLE | Characteristic.WRITEABLE, - CharacteristicValue(read=on_characteristic2_read, write=on_characteristic2_write) + CharacteristicValue( + read=on_characteristic2_read, write=on_characteristic2_write + ), ) service1 = Service( - '3A657F47-D34F-46B3-B1EC-698E29B6B829', - [ - characteristic1, - characteristic2 - ] + '3A657F47-D34F-46B3-B1EC-698E29B6B829', [characteristic1, characteristic2] ) server.add_services([service1]) @@ -265,56 +462,55 @@ async def test_read_write(): await peer.discover_services() await peer.discover_characteristics() c = peer.get_characteristics_by_uuid(characteristic1.uuid) - assert(len(c) == 1) + assert len(c) == 1 c1 = c[0] c = peer.get_characteristics_by_uuid(characteristic2.uuid) - assert(len(c) == 1) + assert len(c) == 1 c2 = c[0] v1 = await peer.read_value(c1) - assert(v1 == b'') + assert v1 == b'' b = bytes([1, 2, 3]) await peer.write_value(c1, b) await async_barrier() - assert(characteristic1.value == b) + assert characteristic1.value == b v1 = await peer.read_value(c1) - assert(v1 == b) - assert(type(characteristic1._last_value) is tuple) - assert(len(characteristic1._last_value) == 2) - assert(str(characteristic1._last_value[0].peer_address) == str(client.random_address)) - assert(characteristic1._last_value[1] == b) + assert v1 == b + assert type(characteristic1._last_value is tuple) + assert len(characteristic1._last_value) == 2 + assert str(characteristic1._last_value[0].peer_address) == str( + client.random_address + ) + assert characteristic1._last_value[1] == b bb = bytes([3, 4, 5, 6]) characteristic1.value = bb v1 = await peer.read_value(c1) - assert(v1 == bb) + assert v1 == bb await peer.write_value(c2, b) await async_barrier() - assert(type(characteristic2._last_value) is tuple) - assert(len(characteristic2._last_value) == 2) - assert(str(characteristic2._last_value[0].peer_address) == str(client.random_address)) - assert(characteristic2._last_value[1] == b) + assert type(characteristic2._last_value is tuple) + assert len(characteristic2._last_value) == 2 + assert str(characteristic2._last_value[0].peer_address) == str( + client.random_address + ) + assert characteristic2._last_value[1] == b # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_read_write2(): - [client, server] = TwoDevices().devices + [client, server] = LinkedDevices().devices[:2] v = bytes([0x11, 0x22, 0x33, 0x44]) characteristic1 = Characteristic( 'FDB159DB-036C-49E3-B3DB-6325AC750806', - Characteristic.READ | Characteristic.WRITE, + Characteristic.Properties.READ | Characteristic.Properties.WRITE, Characteristic.READABLE | Characteristic.WRITEABLE, - value=v + value=v, ) - service1 = Service( - '3A657F47-D34F-46B3-B1EC-698E29B6B829', - [ - characteristic1 - ] - ) + service1 = Service('3A657F47-D34F-46B3-B1EC-698E29B6B829', [characteristic1]) server.add_services([service1]) await client.power_on() @@ -324,81 +520,98 @@ async def test_read_write2(): await peer.discover_services() c = peer.get_services_by_uuid(service1.uuid) - assert(len(c) == 1) + assert len(c) == 1 s = c[0] await s.discover_characteristics() c = s.get_characteristics_by_uuid(characteristic1.uuid) - assert(len(c) == 1) + assert len(c) == 1 c1 = c[0] v1 = await c1.read_value() - assert(v1 == v) + assert v1 == v a1 = PackedCharacteristicAdapter(c1, '>I') v1 = await a1.read_value() - assert(v1 == struct.unpack('>I', v)[0]) + assert v1 == struct.unpack('>I', v)[0] b = bytes([0x55, 0x66, 0x77, 0x88]) await a1.write_value(struct.unpack('>I', b)[0]) await async_barrier() - assert(characteristic1.value == b) + assert characteristic1.value == b v1 = await a1.read_value() - assert(v1 == struct.unpack('>I', b)[0]) + assert v1 == struct.unpack('>I', b)[0] # ----------------------------------------------------------------------------- @pytest.mark.asyncio async def test_subscribe_notify(): - [client, server] = TwoDevices().devices + [client, server] = LinkedDevices().devices[:2] characteristic1 = Characteristic( 'FDB159DB-036C-49E3-B3DB-6325AC750806', - Characteristic.READ | Characteristic.NOTIFY, + Characteristic.Properties.READ | Characteristic.Properties.NOTIFY, Characteristic.READABLE, - bytes([1, 2, 3]) + bytes([1, 2, 3]), ) def on_characteristic1_subscription(connection, notify_enabled, indicate_enabled): - characteristic1._last_subscription = (connection, notify_enabled, indicate_enabled) + characteristic1._last_subscription = ( + connection, + notify_enabled, + indicate_enabled, + ) characteristic1.on('subscription', on_characteristic1_subscription) characteristic2 = Characteristic( '66DE9057-C848-4ACA-B993-D675644EBB85', - Characteristic.READ | Characteristic.INDICATE, + Characteristic.Properties.READ | Characteristic.Properties.INDICATE, Characteristic.READABLE, - bytes([4, 5, 6]) + bytes([4, 5, 6]), ) def on_characteristic2_subscription(connection, notify_enabled, indicate_enabled): - characteristic2._last_subscription = (connection, notify_enabled, indicate_enabled) + characteristic2._last_subscription = ( + connection, + notify_enabled, + indicate_enabled, + ) characteristic2.on('subscription', on_characteristic2_subscription) characteristic3 = Characteristic( 'AB5E639C-40C1-4238-B9CB-AF41F8B806E4', - Characteristic.READ | Characteristic.NOTIFY | Characteristic.INDICATE, + Characteristic.Properties.READ + | Characteristic.Properties.NOTIFY + | Characteristic.Properties.INDICATE, Characteristic.READABLE, - bytes([7, 8, 9]) + bytes([7, 8, 9]), ) def on_characteristic3_subscription(connection, notify_enabled, indicate_enabled): - characteristic3._last_subscription = (connection, notify_enabled, indicate_enabled) + characteristic3._last_subscription = ( + connection, + notify_enabled, + indicate_enabled, + ) characteristic3.on('subscription', on_characteristic3_subscription) service1 = Service( '3A657F47-D34F-46B3-B1EC-698E29B6B829', - [ - characteristic1, - characteristic2, - characteristic3 - ] + [characteristic1, characteristic2, characteristic3], ) server.add_services([service1]) - def on_characteristic_subscription(connection, characteristic, notify_enabled, indicate_enabled): - server._last_subscription = (connection, characteristic, notify_enabled, indicate_enabled) + def on_characteristic_subscription( + connection, characteristic, notify_enabled, indicate_enabled + ): + server._last_subscription = ( + connection, + characteristic, + notify_enabled, + indicate_enabled, + ) server.on('characteristic_subscription', on_characteristic_subscription) @@ -410,68 +623,258 @@ async def test_subscribe_notify(): await peer.discover_services() await peer.discover_characteristics() c = peer.get_characteristics_by_uuid(characteristic1.uuid) - assert(len(c) == 1) + assert len(c) == 1 c1 = c[0] c = peer.get_characteristics_by_uuid(characteristic2.uuid) - assert(len(c) == 1) + assert len(c) == 1 c2 = c[0] c = peer.get_characteristics_by_uuid(characteristic3.uuid) - assert(len(c) == 1) + assert len(c) == 1 c3 = c[0] + c1._called = False c1._last_update = None - def on_c1_update(connection, value): - c1._last_update = (connection, value) + def on_c1_update(value): + c1._called = True + c1._last_update = value c1.on('update', on_c1_update) await peer.subscribe(c1) await async_barrier() - assert(server._last_subscription[1] == characteristic1) - assert(server._last_subscription[2]) - assert(not server._last_subscription[3]) - assert(characteristic1._last_subscription[1]) - assert(not characteristic1._last_subscription[2]) + assert server._last_subscription[1] == characteristic1 + assert server._last_subscription[2] + assert not server._last_subscription[3] + assert characteristic1._last_subscription[1] + assert not characteristic1._last_subscription[2] await server.indicate_subscribers(characteristic1) await async_barrier() - assert(c1._last_update is None) + assert not c1._called await server.notify_subscribers(characteristic1) await async_barrier() - assert(c1._last_update is not None) - assert(c1._last_update[1] == characteristic1.value) + assert c1._called + assert c1._last_update == characteristic1.value + + c1._called = False + c1._last_update = None + c1_value = characteristic1.value + await server.notify_subscribers(characteristic1, bytes([0, 1, 2])) + await async_barrier() + assert c1._called + assert c1._last_update == bytes([0, 1, 2]) + assert characteristic1.value == c1_value + + c1._called = False + await peer.unsubscribe(c1) + await server.notify_subscribers(characteristic1) + assert not c1._called + c2._called = False c2._last_update = None def on_c2_update(value): - c2._last_update = (connection, value) + c2._called = True + c2._last_update = value await peer.subscribe(c2, on_c2_update) await async_barrier() - await server.notify_subscriber(characteristic2._last_subscription[0], characteristic2) + await server.notify_subscriber( + characteristic2._last_subscription[0], characteristic2 + ) + await async_barrier() + assert not c2._called + await server.indicate_subscriber( + characteristic2._last_subscription[0], characteristic2 + ) await async_barrier() - assert(c2._last_update is None) - await server.indicate_subscriber(characteristic2._last_subscription[0], characteristic2) + assert c2._called + assert c2._last_update == characteristic2.value + + c2._called = False + await peer.unsubscribe(c2, on_c2_update) + await server.indicate_subscriber( + characteristic2._last_subscription[0], characteristic2 + ) await async_barrier() - assert(c2._last_update is not None) - assert(c2._last_update[1] == characteristic2.value) + assert not c2._called + c3._called = False + c3._called_2 = False + c3._called_3 = False c3._last_update = None + c3._last_update_2 = None + c3._last_update_3 = None - def on_c3_update(connection, value): - c3._last_update = (connection, value) + def on_c3_update(value): + c3._called = True + c3._last_update = value + + def on_c3_update_2(value): # for notify + c3._called_2 = True + c3._last_update_2 = value + + def on_c3_update_3(value): # for indicate + c3._called_3 = True + c3._last_update_3 = value c3.on('update', on_c3_update) - await peer.subscribe(c3) + await peer.subscribe(c3, on_c3_update_2) + await async_barrier() + await server.notify_subscriber( + characteristic3._last_subscription[0], characteristic3 + ) await async_barrier() - await server.notify_subscriber(characteristic3._last_subscription[0], characteristic3) + assert c3._called + assert c3._last_update == characteristic3.value + assert c3._called_2 + assert c3._last_update_2 == characteristic3.value + assert not c3._called_3 + + c3._called = False + c3._called_2 = False + c3._called_3 = False + await peer.unsubscribe(c3) + await peer.subscribe(c3, on_c3_update_3, prefer_notify=False) await async_barrier() - assert(c3._last_update is not None) - assert(c3._last_update[1] == characteristic3.value) characteristic3.value = bytes([1, 2, 3]) - await server.indicate_subscriber(characteristic3._last_subscription[0], characteristic3) + await server.indicate_subscriber( + characteristic3._last_subscription[0], characteristic3 + ) + await async_barrier() + assert c3._called + assert c3._last_update == characteristic3.value + assert not c3._called_2 + assert c3._called_3 + assert c3._last_update_3 == characteristic3.value + + c3._called = False + c3._called_2 = False + c3._called_3 = False + await peer.unsubscribe(c3) + await server.notify_subscriber( + characteristic3._last_subscription[0], characteristic3 + ) + await server.indicate_subscriber( + characteristic3._last_subscription[0], characteristic3 + ) await async_barrier() - assert(c3._last_update is not None) - assert(c3._last_update[1] == characteristic3.value) + assert not c3._called + assert not c3._called_2 + assert not c3._called_3 + + +# ----------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_mtu_exchange(): + [d1, d2, d3] = LinkedDevices().devices[:3] + + d3.gatt_server.max_mtu = 100 + + d3_connections = [] + + @d3.on('connection') + def on_d3_connection(connection): + d3_connections.append(connection) + + await d1.power_on() + await d2.power_on() + await d3.power_on() + + d1_connection = await d1.connect(d3.random_address) + assert len(d3_connections) == 1 + assert d3_connections[0] is not None + + d2_connection = await d2.connect(d3.random_address) + assert len(d3_connections) == 2 + assert d3_connections[1] is not None + + d1_peer = Peer(d1_connection) + d2_peer = Peer(d2_connection) + + d1_client_mtu = await d1_peer.request_mtu(220) + assert d1_client_mtu == 100 + assert d1_connection.att_mtu == 100 + + d2_client_mtu = await d2_peer.request_mtu(50) + assert d2_client_mtu == 50 + assert d2_connection.att_mtu == 50 + + +# ----------------------------------------------------------------------------- +def test_char_property_to_string(): + # single + assert str(Characteristic.Properties(0x01)) == "Properties.BROADCAST" + assert str(Characteristic.Properties.BROADCAST) == "Properties.BROADCAST" + + # double + assert str(Characteristic.Properties(0x03)) == "Properties.READ|BROADCAST" + assert ( + str(Characteristic.Properties.BROADCAST | Characteristic.Properties.READ) + == "Properties.READ|BROADCAST" + ) + + +# ----------------------------------------------------------------------------- +def test_characteristic_property_from_string(): + # single + assert ( + Characteristic.Properties.from_string("BROADCAST") + == Characteristic.Properties.BROADCAST + ) + + # double + assert ( + Characteristic.Properties.from_string("BROADCAST,READ") + == Characteristic.Properties.BROADCAST | Characteristic.Properties.READ + ) + assert ( + Characteristic.Properties.from_string("READ,BROADCAST") + == Characteristic.Properties.BROADCAST | Characteristic.Properties.READ + ) + + +# ----------------------------------------------------------------------------- +def test_characteristic_property_from_string_assert(): + with pytest.raises(TypeError) as e_info: + Characteristic.Properties.from_string("BROADCAST,HELLO") + + assert ( + str(e_info.value) + == """Characteristic.Properties::from_string() error: +Expected a string containing any of the keys, separated by commas: BROADCAST,READ,WRITE_WITHOUT_RESPONSE,WRITE,NOTIFY,INDICATE,AUTHENTICATED_SIGNED_WRITES,EXTENDED_PROPERTIES +Got: BROADCAST,HELLO""" + ) + + +# ----------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_server_string(): + [_, server] = LinkedDevices().devices[:2] + + characteristic = Characteristic( + 'FDB159DB-036C-49E3-B3DB-6325AC750806', + Characteristic.Properties.READ + | Characteristic.Properties.WRITE + | Characteristic.Properties.NOTIFY, + Characteristic.READABLE | Characteristic.WRITEABLE, + bytes([123]), + ) + + service = Service('3A657F47-D34F-46B3-B1EC-698E29B6B829', [characteristic]) + server.add_service(service) + + assert ( + str(server.gatt_server) + == """Service(handle=0x0001, end=0x0005, uuid=UUID-16:1800 (Generic Access)) +CharacteristicDeclaration(handle=0x0002, value_handle=0x0003, uuid=UUID-16:2A00 (Device Name), Properties.READ) +Characteristic(handle=0x0003, end=0x0003, uuid=UUID-16:2A00 (Device Name), Properties.READ) +CharacteristicDeclaration(handle=0x0004, value_handle=0x0005, uuid=UUID-16:2A01 (Appearance), Properties.READ) +Characteristic(handle=0x0005, end=0x0005, uuid=UUID-16:2A01 (Appearance), Properties.READ) +Service(handle=0x0006, end=0x0009, uuid=3A657F47-D34F-46B3-B1EC-698E29B6B829) +CharacteristicDeclaration(handle=0x0007, value_handle=0x0008, uuid=FDB159DB-036C-49E3-B3DB-6325AC750806, Properties.NOTIFY|WRITE|READ) +Characteristic(handle=0x0008, end=0x0009, uuid=FDB159DB-036C-49E3-B3DB-6325AC750806, Properties.NOTIFY|WRITE|READ) +Descriptor(handle=0x0009, type=UUID-16:2902 (Client Characteristic Configuration), value=0000)""" + ) # ----------------------------------------------------------------------------- @@ -479,10 +882,146 @@ async def async_main(): await test_read_write() await test_read_write2() await test_subscribe_notify() + await test_characteristic_encoding() + await test_mtu_exchange() + + +# ----------------------------------------------------------------------------- +def test_attribute_string_to_permissions(): + assert Attribute.string_to_permissions('READABLE') == 1 + assert Attribute.string_to_permissions('WRITEABLE') == 2 + assert Attribute.string_to_permissions('READABLE,WRITEABLE') == 3 + + +# ----------------------------------------------------------------------------- +def test_characteristic_permissions(): + characteristic = Characteristic( + 'FDB159DB-036C-49E3-B3DB-6325AC750806', + Characteristic.Properties.READ + | Characteristic.Properties.WRITE + | Characteristic.Properties.NOTIFY, + 'READABLE,WRITEABLE', + ) + assert characteristic.permissions == 3 + + +# ----------------------------------------------------------------------------- +def test_characteristic_has_properties(): + characteristic = Characteristic( + 'FDB159DB-036C-49E3-B3DB-6325AC750806', + Characteristic.Properties.READ + | Characteristic.Properties.WRITE + | Characteristic.Properties.NOTIFY, + 'READABLE,WRITEABLE', + ) + assert characteristic.has_properties(Characteristic.Properties.READ) + assert characteristic.has_properties( + Characteristic.Properties.READ | Characteristic.Properties.WRITE + ) + assert not characteristic.has_properties( + Characteristic.Properties.READ + | Characteristic.Properties.WRITE + | Characteristic.Properties.INDICATE + ) + assert not characteristic.has_properties(Characteristic.Properties.INDICATE) + + +# ----------------------------------------------------------------------------- +def test_descriptor_permissions(): + descriptor = Descriptor('2902', 'READABLE,WRITEABLE') + assert descriptor.permissions == 3 + + +# ----------------------------------------------------------------------------- +def test_get_attribute_group(): + device = Device() + + # add some services / characteristics to the gatt server + characteristic1 = Characteristic( + '1111', + Characteristic.READ | Characteristic.WRITE | Characteristic.NOTIFY, + Characteristic.READABLE | Characteristic.WRITEABLE, + bytes([123]), + ) + characteristic2 = Characteristic( + '2222', + Characteristic.READ | Characteristic.WRITE | Characteristic.NOTIFY, + Characteristic.READABLE | Characteristic.WRITEABLE, + bytes([123]), + ) + services = [Service('1212', [characteristic1]), Service('3233', [characteristic2])] + device.gatt_server.add_services(services) + + # get the handles from gatt server + characteristic_attributes1 = device.gatt_server.get_characteristic_attributes( + UUID('1212'), UUID('1111') + ) + assert characteristic_attributes1 is not None + characteristic_attributes2 = device.gatt_server.get_characteristic_attributes( + UUID('3233'), UUID('2222') + ) + assert characteristic_attributes2 is not None + descriptor1 = device.gatt_server.get_descriptor_attribute( + UUID('1212'), UUID('1111'), UUID('2902') + ) + assert descriptor1 is not None + descriptor2 = device.gatt_server.get_descriptor_attribute( + UUID('3233'), UUID('2222'), UUID('2902') + ) + assert descriptor2 is not None + + # confirm the handles map back to the service + assert ( + UUID('1212') + == device.gatt_server.get_attribute_group( + characteristic_attributes1[0].handle, Service + ).uuid + ) + assert ( + UUID('1212') + == device.gatt_server.get_attribute_group( + characteristic_attributes1[1].handle, Service + ).uuid + ) + assert ( + UUID('1212') + == device.gatt_server.get_attribute_group(descriptor1.handle, Service).uuid + ) + assert ( + UUID('3233') + == device.gatt_server.get_attribute_group( + characteristic_attributes2[0].handle, Service + ).uuid + ) + assert ( + UUID('3233') + == device.gatt_server.get_attribute_group( + characteristic_attributes2[1].handle, Service + ).uuid + ) + assert ( + UUID('3233') + == device.gatt_server.get_attribute_group(descriptor2.handle, Service).uuid + ) + + # confirm the handles map back to the characteristic + assert ( + UUID('1111') + == device.gatt_server.get_attribute_group( + descriptor1.handle, Characteristic + ).uuid + ) + assert ( + UUID('2222') + == device.gatt_server.get_attribute_group( + descriptor2.handle, Characteristic + ).uuid + ) + # ----------------------------------------------------------------------------- if __name__ == '__main__': - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) test_UUID() test_ATT_Error_Response() test_ATT_Read_By_Group_Type_Request() diff --git a/tests/hci_test.py b/tests/hci_test.py index c4da67e..af68e86 100644 --- a/tests/hci_test.py +++ b/tests/hci_test.py @@ -15,10 +15,56 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- -from bumble.hci import * + +from bumble.hci import ( + HCI_DISCONNECT_COMMAND, + HCI_LE_1M_PHY_BIT, + HCI_LE_CODED_PHY_BIT, + HCI_LE_READ_BUFFER_SIZE_COMMAND, + HCI_RESET_COMMAND, + HCI_SUCCESS, + Address, + HCI_Command, + HCI_Command_Complete_Event, + HCI_Command_Status_Event, + HCI_CustomPacket, + HCI_Disconnect_Command, + HCI_Event, + HCI_LE_Add_Device_To_Filter_Accept_List_Command, + HCI_LE_Advertising_Report_Event, + HCI_LE_Channel_Selection_Algorithm_Event, + HCI_LE_Connection_Complete_Event, + HCI_LE_Connection_Update_Command, + HCI_LE_Connection_Update_Complete_Event, + HCI_LE_Create_Connection_Command, + HCI_LE_Extended_Create_Connection_Command, + HCI_LE_Read_Buffer_Size_Command, + HCI_LE_Read_Remote_Features_Command, + HCI_LE_Read_Remote_Features_Complete_Event, + HCI_LE_Remove_Device_From_Filter_Accept_List_Command, + HCI_LE_Set_Advertising_Data_Command, + HCI_LE_Set_Advertising_Parameters_Command, + HCI_LE_Set_Default_PHY_Command, + HCI_LE_Set_Event_Mask_Command, + HCI_LE_Set_Extended_Scan_Parameters_Command, + HCI_LE_Set_Random_Address_Command, + HCI_LE_Set_Scan_Enable_Command, + HCI_LE_Set_Scan_Parameters_Command, + HCI_Number_Of_Completed_Packets_Event, + HCI_Packet, + HCI_PIN_Code_Request_Reply_Command, + HCI_Read_Local_Supported_Commands_Command, + HCI_Read_Local_Supported_Features_Command, + HCI_Read_Local_Version_Information_Command, + HCI_Reset_Command, + HCI_Set_Event_Mask_Command, +) + + +# ----------------------------------------------------------------------------- +# pylint: disable=invalid-name -# ----------------------------------------------------------------------------- def basic_check(x): packet = x.to_bytes() print(packet.hex()) @@ -27,8 +73,8 @@ def basic_check(x): parsed_str = str(parsed) print(x_str) parsed_bytes = parsed.to_bytes() - assert(x_str == parsed_str) - assert(packet == parsed_bytes) + assert x_str == parsed_str + assert packet == parsed_bytes # ----------------------------------------------------------------------------- @@ -44,29 +90,31 @@ def test_HCI_Event(): def test_HCI_LE_Connection_Complete_Event(): address = Address('00:11:22:33:44:55') event = HCI_LE_Connection_Complete_Event( - status = HCI_SUCCESS, - connection_handle = 1, - role = 1, - peer_address_type = 1, - peer_address = address, - conn_interval = 3, - conn_latency = 4, - supervision_timeout = 5, - master_clock_accuracy = 6 + status=HCI_SUCCESS, + connection_handle=1, + role=1, + peer_address_type=1, + peer_address=address, + connection_interval=3, + peripheral_latency=4, + supervision_timeout=5, + central_clock_accuracy=6, ) basic_check(event) # ----------------------------------------------------------------------------- def test_HCI_LE_Advertising_Report_Event(): - address = Address('00:11:22:33:44:55') - report = HCI_Object( - HCI_LE_Advertising_Report_Event.REPORT_FIELDS, - event_type = HCI_LE_Advertising_Report_Event.ADV_IND, - address_type = Address.PUBLIC_DEVICE_ADDRESS, - address = address, - data = bytes.fromhex('0201061106ba5689a6fabfa2bd01467d6e00fbabad08160a181604659b03'), - rssi = 100 + address = Address('00:11:22:33:44:55/P') + report = HCI_LE_Advertising_Report_Event.Report( + HCI_LE_Advertising_Report_Event.Report.FIELDS, + event_type=HCI_LE_Advertising_Report_Event.ADV_IND, + address_type=Address.PUBLIC_DEVICE_ADDRESS, + address=address, + data=bytes.fromhex( + '0201061106ba5689a6fabfa2bd01467d6e00fbabad08160a181604659b03' + ), + rssi=100, ) event = HCI_LE_Advertising_Report_Event([report]) basic_check(event) @@ -75,9 +123,9 @@ def test_HCI_LE_Advertising_Report_Event(): # ----------------------------------------------------------------------------- def test_HCI_LE_Read_Remote_Features_Complete_Event(): event = HCI_LE_Read_Remote_Features_Complete_Event( - status = HCI_SUCCESS, - connection_handle = 0x007, - le_features = bytes.fromhex('0011223344556677') + status=HCI_SUCCESS, + connection_handle=0x007, + le_features=bytes.fromhex('0011223344556677'), ) basic_check(event) @@ -85,11 +133,11 @@ def test_HCI_LE_Read_Remote_Features_Complete_Event(): # ----------------------------------------------------------------------------- def test_HCI_LE_Connection_Update_Complete_Event(): event = HCI_LE_Connection_Update_Complete_Event( - status = HCI_SUCCESS, - connection_handle = 0x007, - conn_interval = 10, - conn_latency = 3, - supervision_timeout = 5 + status=HCI_SUCCESS, + connection_handle=0x007, + connection_interval=10, + peripheral_latency=3, + supervision_timeout=5, ) basic_check(event) @@ -97,8 +145,7 @@ def test_HCI_LE_Connection_Update_Complete_Event(): # ----------------------------------------------------------------------------- def test_HCI_LE_Channel_Selection_Algorithm_Event(): event = HCI_LE_Channel_Selection_Algorithm_Event( - connection_handle = 7, - channel_selection_algorithm = 1 + connection_handle=7, channel_selection_algorithm=1 ) basic_check(event) @@ -107,60 +154,53 @@ def test_HCI_LE_Channel_Selection_Algorithm_Event(): def test_HCI_Command_Complete_Event(): # With a serializable object event = HCI_Command_Complete_Event( - num_hci_command_packets = 34, - command_opcode = HCI_LE_READ_BUFFER_SIZE_COMMAND, - return_parameters = HCI_LE_Read_Buffer_Size_Command.create_return_parameters( - status = 0, - hc_le_acl_data_packet_length = 1234, - hc_total_num_le_acl_data_packets = 56 - ) + num_hci_command_packets=34, + command_opcode=HCI_LE_READ_BUFFER_SIZE_COMMAND, + return_parameters=HCI_LE_Read_Buffer_Size_Command.create_return_parameters( + status=0, + hc_le_acl_data_packet_length=1234, + hc_total_num_le_acl_data_packets=56, + ), ) basic_check(event) # With an arbitrary byte array event = HCI_Command_Complete_Event( - num_hci_command_packets = 1, - command_opcode = HCI_RESET_COMMAND, - return_parameters = bytes([1, 2, 3, 4]) + num_hci_command_packets=1, + command_opcode=HCI_RESET_COMMAND, + return_parameters=bytes([1, 2, 3, 4]), ) basic_check(event) # With a simple status as a 1-byte array event = HCI_Command_Complete_Event( - num_hci_command_packets = 1, - command_opcode = HCI_RESET_COMMAND, - return_parameters = bytes([7]) + num_hci_command_packets=1, + command_opcode=HCI_RESET_COMMAND, + return_parameters=bytes([7]), ) basic_check(event) event = HCI_Packet.from_bytes(event.to_bytes()) - assert(event.return_parameters == 7) + assert event.return_parameters == 7 # With a simple status as an integer status event = HCI_Command_Complete_Event( - num_hci_command_packets = 1, - command_opcode = HCI_RESET_COMMAND, - return_parameters = 9 + num_hci_command_packets=1, command_opcode=HCI_RESET_COMMAND, return_parameters=9 ) basic_check(event) - assert(event.return_parameters == 9) + assert event.return_parameters == 9 # ----------------------------------------------------------------------------- def test_HCI_Command_Status_Event(): event = HCI_Command_Status_Event( - status = 0, - num_hci_command_packets = 37, - command_opcode = HCI_DISCONNECT_COMMAND + status=0, num_hci_command_packets=37, command_opcode=HCI_DISCONNECT_COMMAND ) basic_check(event) # ----------------------------------------------------------------------------- def test_HCI_Number_Of_Completed_Packets_Event(): - event = HCI_Number_Of_Completed_Packets_Event([ - (1, 2), - (3, 4) - ]) + event = HCI_Number_Of_Completed_Packets_Event([(1, 2), (3, 4)]) basic_check(event) @@ -174,6 +214,23 @@ def test_HCI_Command(): # ----------------------------------------------------------------------------- +def test_HCI_PIN_Code_Request_Reply_Command(): + pin_code = b'1234' + pin_code_length = len(pin_code) + # here to make the test pass, we need to + # pad pin_code, as HCI_Object.format_fields + # does not do it for us + padded_pin_code = pin_code + bytes(16 - pin_code_length) + command = HCI_PIN_Code_Request_Reply_Command( + bd_addr=Address( + '00:11:22:33:44:55', address_type=Address.PUBLIC_DEVICE_ADDRESS + ), + pin_code_length=pin_code_length, + pin_code=padded_pin_code, + ) + basic_check(command) + + def test_HCI_Reset_Command(): command = HCI_Reset_Command() basic_check(command) @@ -199,25 +256,20 @@ def test_HCI_Read_Local_Supported_Features_Command(): # ----------------------------------------------------------------------------- def test_HCI_Disconnect_Command(): - command = HCI_Disconnect_Command( - connection_handle = 123, - reason = 0x11 - ) + command = HCI_Disconnect_Command(connection_handle=123, reason=0x11) basic_check(command) # ----------------------------------------------------------------------------- def test_HCI_Set_Event_Mask_Command(): - command = HCI_Set_Event_Mask_Command( - event_mask = bytes.fromhex('0011223344556677') - ) + command = HCI_Set_Event_Mask_Command(event_mask=bytes.fromhex('0011223344556677')) basic_check(command) # ----------------------------------------------------------------------------- def test_HCI_LE_Set_Event_Mask_Command(): command = HCI_LE_Set_Event_Mask_Command( - le_event_mask = bytes.fromhex('0011223344556677') + le_event_mask=bytes.fromhex('0011223344556677') ) basic_check(command) @@ -225,7 +277,7 @@ def test_HCI_LE_Set_Event_Mask_Command(): # ----------------------------------------------------------------------------- def test_HCI_LE_Set_Random_Address_Command(): command = HCI_LE_Set_Random_Address_Command( - random_address = Address('00:11:22:33:44:55') + random_address=Address('00:11:22:33:44:55') ) basic_check(command) @@ -233,14 +285,14 @@ def test_HCI_LE_Set_Random_Address_Command(): # ----------------------------------------------------------------------------- def test_HCI_LE_Set_Advertising_Parameters_Command(): command = HCI_LE_Set_Advertising_Parameters_Command( - advertising_interval_min = 20, - advertising_interval_max = 30, - advertising_type = HCI_LE_Set_Advertising_Parameters_Command.ADV_NONCONN_IND, - own_address_type = Address.PUBLIC_DEVICE_ADDRESS, - peer_address_type = Address.RANDOM_DEVICE_ADDRESS, - peer_address = Address('00:11:22:33:44:55'), - advertising_channel_map = 0x03, - advertising_filter_policy = 1 + advertising_interval_min=20, + advertising_interval_max=30, + advertising_type=HCI_LE_Set_Advertising_Parameters_Command.ADV_NONCONN_IND, + own_address_type=Address.PUBLIC_DEVICE_ADDRESS, + peer_address_type=Address.RANDOM_DEVICE_ADDRESS, + peer_address=Address('00:11:22:33:44:55'), + advertising_channel_map=0x03, + advertising_filter_policy=1, ) basic_check(command) @@ -248,7 +300,7 @@ def test_HCI_LE_Set_Advertising_Parameters_Command(): # ----------------------------------------------------------------------------- def test_HCI_LE_Set_Advertising_Data_Command(): command = HCI_LE_Set_Advertising_Data_Command( - advertising_data = bytes.fromhex('AABBCC') + advertising_data=bytes.fromhex('AABBCC') ) basic_check(command) @@ -256,57 +308,72 @@ def test_HCI_LE_Set_Advertising_Data_Command(): # ----------------------------------------------------------------------------- def test_HCI_LE_Set_Scan_Parameters_Command(): command = HCI_LE_Set_Scan_Parameters_Command( - le_scan_type = 1, - le_scan_interval = 20, - le_scan_window = 10, - own_address_type = 1, - scanning_filter_policy = 0 + le_scan_type=1, + le_scan_interval=20, + le_scan_window=10, + own_address_type=1, + scanning_filter_policy=0, ) basic_check(command) # ----------------------------------------------------------------------------- def test_HCI_LE_Set_Scan_Enable_Command(): - command = HCI_LE_Set_Scan_Enable_Command( - le_scan_enable = 1, - filter_duplicates = 0 - ) + command = HCI_LE_Set_Scan_Enable_Command(le_scan_enable=1, filter_duplicates=0) basic_check(command) # ----------------------------------------------------------------------------- def test_HCI_LE_Create_Connection_Command(): command = HCI_LE_Create_Connection_Command( - le_scan_interval = 4, - le_scan_window = 5, - initiator_filter_policy = 1, - peer_address_type = 1, - peer_address = Address('00:11:22:33:44:55'), - own_address_type = 2, - conn_interval_min = 7, - conn_interval_max = 8, - conn_latency = 9, - supervision_timeout = 10, - minimum_ce_length = 11, - maximum_ce_length = 12 + le_scan_interval=4, + le_scan_window=5, + initiator_filter_policy=1, + peer_address_type=1, + peer_address=Address('00:11:22:33:44:55'), + own_address_type=2, + connection_interval_min=7, + connection_interval_max=8, + max_latency=9, + supervision_timeout=10, + min_ce_length=11, + max_ce_length=12, ) basic_check(command) # ----------------------------------------------------------------------------- -def test_HCI_LE_Add_Device_To_White_List_Command(): - command = HCI_LE_Add_Device_To_White_List_Command( - address_type = 1, - address = Address('00:11:22:33:44:55') +def test_HCI_LE_Extended_Create_Connection_Command(): + command = HCI_LE_Extended_Create_Connection_Command( + initiator_filter_policy=0, + own_address_type=0, + peer_address_type=1, + peer_address=Address('00:11:22:33:44:55'), + initiating_phys=3, + scan_intervals=(10, 11), + scan_windows=(12, 13), + connection_interval_mins=(14, 15), + connection_interval_maxs=(16, 17), + max_latencies=(18, 19), + supervision_timeouts=(20, 21), + min_ce_lengths=(100, 101), + max_ce_lengths=(102, 103), ) basic_check(command) # ----------------------------------------------------------------------------- -def test_HCI_LE_Remove_Device_From_White_List_Command(): - command = HCI_LE_Remove_Device_From_White_List_Command( - address_type = 1, - address = Address('00:11:22:33:44:55') +def test_HCI_LE_Add_Device_To_Filter_Accept_List_Command(): + command = HCI_LE_Add_Device_To_Filter_Accept_List_Command( + address_type=1, address=Address('00:11:22:33:44:55') + ) + basic_check(command) + + +# ----------------------------------------------------------------------------- +def test_HCI_LE_Remove_Device_From_Filter_Accept_List_Command(): + command = HCI_LE_Remove_Device_From_Filter_Accept_List_Command( + address_type=1, address=Address('00:11:22:33:44:55') ) basic_check(command) @@ -314,31 +381,43 @@ def test_HCI_LE_Remove_Device_From_White_List_Command(): # ----------------------------------------------------------------------------- def test_HCI_LE_Connection_Update_Command(): command = HCI_LE_Connection_Update_Command( - connection_handle = 0x0002, - conn_interval_min = 10, - conn_interval_max = 20, - conn_latency = 7, - supervision_timeout = 3, - minimum_ce_length = 100, - maximum_ce_length = 200 + connection_handle=0x0002, + connection_interval_min=10, + connection_interval_max=20, + max_latency=7, + supervision_timeout=3, + min_ce_length=100, + max_ce_length=200, ) basic_check(command) # ----------------------------------------------------------------------------- def test_HCI_LE_Read_Remote_Features_Command(): - command = HCI_LE_Read_Remote_Features_Command( - connection_handle = 0x0002 - ) + command = HCI_LE_Read_Remote_Features_Command(connection_handle=0x0002) basic_check(command) # ----------------------------------------------------------------------------- def test_HCI_LE_Set_Default_PHY_Command(): - command = HCI_LE_Set_Default_PHY_Command( - all_phys = 0, - tx_phys = 1, - rx_phys = 1 + command = HCI_LE_Set_Default_PHY_Command(all_phys=0, tx_phys=1, rx_phys=1) + basic_check(command) + + +# ----------------------------------------------------------------------------- +def test_HCI_LE_Set_Extended_Scan_Parameters_Command(): + command = HCI_LE_Set_Extended_Scan_Parameters_Command( + own_address_type=Address.RANDOM_DEVICE_ADDRESS, + # pylint: disable-next=line-too-long + scanning_filter_policy=HCI_LE_Set_Extended_Scan_Parameters_Command.BASIC_FILTERED_POLICY, + scanning_phys=(1 << HCI_LE_1M_PHY_BIT | 1 << HCI_LE_CODED_PHY_BIT | 1 << 4), + scan_types=[ + HCI_LE_Set_Extended_Scan_Parameters_Command.ACTIVE_SCANNING, + HCI_LE_Set_Extended_Scan_Parameters_Command.ACTIVE_SCANNING, + HCI_LE_Set_Extended_Scan_Parameters_Command.PASSIVE_SCANNING, + ], + scan_intervals=[1, 2, 3], + scan_windows=[4, 5, 6], ) basic_check(command) @@ -346,20 +425,20 @@ def test_HCI_LE_Set_Default_PHY_Command(): # ----------------------------------------------------------------------------- def test_address(): a = Address('C4:F2:17:1A:1D:BB') - assert(not a.is_public) - assert(a.is_random) - assert(a.address_type == Address.RANDOM_DEVICE_ADDRESS) - assert(not a.is_resolvable) - assert(not a.is_resolved) - assert(a.is_static) + assert not a.is_public + assert a.is_random + assert a.address_type == Address.RANDOM_DEVICE_ADDRESS + assert not a.is_resolvable + assert not a.is_resolved + assert a.is_static # ----------------------------------------------------------------------------- def test_custom(): data = bytes([0x77, 0x02, 0x01, 0x03]) packet = HCI_CustomPacket(data) - assert(packet.hci_packet_type == 0x77) - assert(packet.payload == data) + assert packet.hci_packet_type == 0x77 + assert packet.payload == data # ----------------------------------------------------------------------------- @@ -379,6 +458,7 @@ def run_test_events(): def run_test_commands(): test_HCI_Command() test_HCI_Reset_Command() + test_HCI_PIN_Code_Request_Reply_Command() test_HCI_Read_Local_Version_Information_Command() test_HCI_Read_Local_Supported_Commands_Command() test_HCI_Read_Local_Supported_Features_Command() @@ -391,11 +471,13 @@ def run_test_commands(): test_HCI_LE_Set_Scan_Parameters_Command() test_HCI_LE_Set_Scan_Enable_Command() test_HCI_LE_Create_Connection_Command() - test_HCI_LE_Add_Device_To_White_List_Command() - test_HCI_LE_Remove_Device_From_White_List_Command() + test_HCI_LE_Extended_Create_Connection_Command() + test_HCI_LE_Add_Device_To_Filter_Accept_List_Command() + test_HCI_LE_Remove_Device_From_Filter_Accept_List_Command() test_HCI_LE_Connection_Update_Command() test_HCI_LE_Read_Remote_Features_Command() test_HCI_LE_Set_Default_PHY_Command() + test_HCI_LE_Set_Extended_Scan_Parameters_Command() # ----------------------------------------------------------------------------- diff --git a/tests/import_test.py b/tests/import_test.py index d6eafbd..e0b6e3c 100644 --- a/tests/import_test.py +++ b/tests/import_test.py @@ -38,7 +38,7 @@ def test_import(): sdp, smp, transport, - utils + utils, ) assert att @@ -63,5 +63,67 @@ def test_import(): # ----------------------------------------------------------------------------- +def test_app_imports(): + from apps.console import main + + assert main + + from apps.controller_info import main + + assert main + + from apps.controllers import main + + assert main + + from apps.gatt_dump import main + + assert main + + from apps.gg_bridge import main + + assert main + + from apps.hci_bridge import main + + assert main + + from apps.pair import main + + assert main + + from apps.scan import main + + assert main + + from apps.show import main + + assert main + + from apps.unbond import main + + assert main + + from apps.usb_probe import main + + assert main + + +# ----------------------------------------------------------------------------- +def test_profiles_imports(): + from bumble.profiles import ( + battery_service, + device_information_service, + heart_rate_service, + ) + + assert battery_service + assert device_information_service + assert heart_rate_service + + +# ----------------------------------------------------------------------------- if __name__ == '__main__': test_import() + test_app_imports() + test_profiles_imports() diff --git a/tests/l2cap_test.py b/tests/l2cap_test.py new file mode 100644 index 0000000..6f8e181 --- /dev/null +++ b/tests/l2cap_test.py @@ -0,0 +1,279 @@ +# Copyright 2021-2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ----------------------------------------------------------------------------- +# Imports +# ----------------------------------------------------------------------------- +import asyncio +import logging +import os +import random +import pytest + +from bumble.controller import Controller +from bumble.link import LocalLink +from bumble.device import Device +from bumble.host import Host +from bumble.transport import AsyncPipeSink +from bumble.core import ProtocolError +from bumble.l2cap import L2CAP_Connection_Request + + +# ----------------------------------------------------------------------------- +# Logging +# ----------------------------------------------------------------------------- +logger = logging.getLogger(__name__) + + +# ----------------------------------------------------------------------------- +class TwoDevices: + def __init__(self): + self.connections = [None, None] + + self.link = LocalLink() + self.controllers = [ + Controller('C1', link=self.link), + Controller('C2', link=self.link), + ] + self.devices = [ + Device( + address='F0:F1:F2:F3:F4:F5', + host=Host(self.controllers[0], AsyncPipeSink(self.controllers[0])), + ), + Device( + address='F5:F4:F3:F2:F1:F0', + host=Host(self.controllers[1], AsyncPipeSink(self.controllers[1])), + ), + ] + + self.paired = [None, None] + + def on_connection(self, which, connection): + self.connections[which] = connection + + def on_paired(self, which, keys): + self.paired[which] = keys + + +# ----------------------------------------------------------------------------- +async def setup_connection(): + # Create two devices, each with a controller, attached to the same link + two_devices = TwoDevices() + + # Attach listeners + two_devices.devices[0].on( + 'connection', lambda connection: two_devices.on_connection(0, connection) + ) + two_devices.devices[1].on( + 'connection', lambda connection: two_devices.on_connection(1, connection) + ) + + # Start + await two_devices.devices[0].power_on() + await two_devices.devices[1].power_on() + + # Connect the two devices + await two_devices.devices[0].connect(two_devices.devices[1].random_address) + + # Check the post conditions + assert two_devices.connections[0] is not None + assert two_devices.connections[1] is not None + + return two_devices + + +# ----------------------------------------------------------------------------- +def test_helpers(): + psm = L2CAP_Connection_Request.serialize_psm(0x01) + assert psm == bytes([0x01, 0x00]) + + psm = L2CAP_Connection_Request.serialize_psm(0x1023) + assert psm == bytes([0x23, 0x10]) + + psm = L2CAP_Connection_Request.serialize_psm(0x242311) + assert psm == bytes([0x11, 0x23, 0x24]) + + (offset, psm) = L2CAP_Connection_Request.parse_psm( + bytes([0x00, 0x01, 0x00, 0x44]), 1 + ) + assert offset == 3 + assert psm == 0x01 + + (offset, psm) = L2CAP_Connection_Request.parse_psm( + bytes([0x00, 0x23, 0x10, 0x44]), 1 + ) + assert offset == 3 + assert psm == 0x1023 + + (offset, psm) = L2CAP_Connection_Request.parse_psm( + bytes([0x00, 0x11, 0x23, 0x24, 0x44]), 1 + ) + assert offset == 4 + assert psm == 0x242311 + + rq = L2CAP_Connection_Request(psm=0x01, source_cid=0x44) + brq = bytes(rq) + srq = L2CAP_Connection_Request.from_bytes(brq) + assert srq.psm == rq.psm + assert srq.source_cid == rq.source_cid + + +# ----------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_basic_connection(): + devices = await setup_connection() + psm = 1234 + + # Check that if there's no one listening, we can't connect + with pytest.raises(ProtocolError): + l2cap_channel = await devices.connections[0].open_l2cap_channel(psm) + + # Now add a listener + incoming_channel = None + received = [] + + def on_coc(channel): + nonlocal incoming_channel + incoming_channel = channel + + def on_data(data): + received.append(data) + + channel.sink = on_data + + devices.devices[1].register_l2cap_channel_server(psm, on_coc) + l2cap_channel = await devices.connections[0].open_l2cap_channel(psm) + + messages = (bytes([1, 2, 3]), bytes([4, 5, 6]), bytes(10000)) + for message in messages: + l2cap_channel.write(message) + await asyncio.sleep(0) + + await l2cap_channel.drain() + + # Test closing + closed = [False, False] + closed_event = asyncio.Event() + + def on_close(which, event): + closed[which] = True + if event: + event.set() + + l2cap_channel.on('close', lambda: on_close(0, None)) + incoming_channel.on('close', lambda: on_close(1, closed_event)) + await l2cap_channel.disconnect() + assert closed == [True, True] + await closed_event.wait() + + sent_bytes = b''.join(messages) + received_bytes = b''.join(received) + assert sent_bytes == received_bytes + + +# ----------------------------------------------------------------------------- +async def transfer_payload(max_credits, mtu, mps): + devices = await setup_connection() + + received = [] + + def on_coc(channel): + def on_data(data): + received.append(data) + + channel.sink = on_data + + psm = devices.devices[1].register_l2cap_channel_server( + psm=0, server=on_coc, max_credits=max_credits, mtu=mtu, mps=mps + ) + l2cap_channel = await devices.connections[0].open_l2cap_channel(psm) + + messages = [bytes([1, 2, 3, 4, 5, 6, 7]) * x for x in (3, 10, 100, 789)] + for message in messages: + l2cap_channel.write(message) + await asyncio.sleep(0) + if random.randint(0, 5) == 1: + await l2cap_channel.drain() + + await l2cap_channel.drain() + await l2cap_channel.disconnect() + + sent_bytes = b''.join(messages) + received_bytes = b''.join(received) + assert sent_bytes == received_bytes + + +@pytest.mark.asyncio +async def test_transfer(): + for max_credits in (1, 10, 100, 10000): + for mtu in (50, 255, 256, 1000): + for mps in (50, 255, 256, 1000): + # print(max_credits, mtu, mps) + await transfer_payload(max_credits, mtu, mps) + + +# ----------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_bidirectional_transfer(): + devices = await setup_connection() + + client_received = [] + server_received = [] + server_channel = None + + def on_server_coc(channel): + nonlocal server_channel + server_channel = channel + + def on_server_data(data): + server_received.append(data) + + channel.sink = on_server_data + + def on_client_data(data): + client_received.append(data) + + psm = devices.devices[1].register_l2cap_channel_server(psm=0, server=on_server_coc) + client_channel = await devices.connections[0].open_l2cap_channel(psm) + client_channel.sink = on_client_data + + messages = [bytes([1, 2, 3, 4, 5, 6, 7]) * x for x in (3, 10, 100)] + for message in messages: + client_channel.write(message) + await client_channel.drain() + await asyncio.sleep(0) + server_channel.write(message) + await server_channel.drain() + + await client_channel.disconnect() + + message_bytes = b''.join(messages) + client_received_bytes = b''.join(client_received) + server_received_bytes = b''.join(server_received) + assert client_received_bytes == message_bytes + assert server_received_bytes == message_bytes + + +# ----------------------------------------------------------------------------- +async def run(): + test_helpers() + await test_basic_connection() + await test_transfer() + await test_bidirectional_transfer() + + +# ----------------------------------------------------------------------------- +if __name__ == '__main__': + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + asyncio.run(run()) diff --git a/tests/pytest.ini b/tests/pytest.ini index 6796337..13c0bbf 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -1,3 +1,3 @@ [pytest] junit_logging = all -asyncio_mode = auto
\ No newline at end of file +asyncio_mode = auto diff --git a/tests/rfcomm_test.py b/tests/rfcomm_test.py index 6f1c44f..9465462 100644 --- a/tests/rfcomm_test.py +++ b/tests/rfcomm_test.py @@ -30,10 +30,10 @@ def basic_frame_check(x): parsed_bytes = bytes(parsed) if len(serialized) < 500: print('Parsed Bytes:', parsed_bytes.hex()) - assert(parsed_bytes == serialized) + assert parsed_bytes == serialized x_str = str(x) parsed_str = str(parsed) - assert(x_str == parsed_str) + assert x_str == parsed_str # ----------------------------------------------------------------------------- diff --git a/tests/sdp_test.py b/tests/sdp_test.py index d7cf8be..f07b579 100644 --- a/tests/sdp_test.py +++ b/tests/sdp_test.py @@ -15,9 +15,12 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- -from bumble.sdp import * from bumble.core import UUID +from bumble.sdp import DataElement +# ----------------------------------------------------------------------------- +# pylint: disable=invalid-name +# ----------------------------------------------------------------------------- # ----------------------------------------------------------------------------- def basic_check(x): @@ -31,10 +34,10 @@ def basic_check(x): parsed_bytes = bytes(parsed) if len(serialized) < 500: print('Parsed Bytes:', parsed_bytes.hex()) - assert(parsed_bytes == serialized) + assert parsed_bytes == serialized x_str = str(x) parsed_str = str(parsed) - assert(x_str == parsed_str) + assert x_str == parsed_str # ----------------------------------------------------------------------------- @@ -99,19 +102,25 @@ def test_data_elements(): e = DataElement(DataElement.SEQUENCE, [DataElement(DataElement.BOOLEAN, True)]) basic_check(e) - e = DataElement(DataElement.SEQUENCE, [ - DataElement(DataElement.BOOLEAN, True), - DataElement(DataElement.TEXT_STRING, 'hello') - ]) + e = DataElement( + DataElement.SEQUENCE, + [ + DataElement(DataElement.BOOLEAN, True), + DataElement(DataElement.TEXT_STRING, 'hello'), + ], + ) basic_check(e) e = DataElement(DataElement.ALTERNATIVE, [DataElement(DataElement.BOOLEAN, True)]) basic_check(e) - e = DataElement(DataElement.ALTERNATIVE, [ - DataElement(DataElement.BOOLEAN, True), - DataElement(DataElement.TEXT_STRING, 'hello') - ]) + e = DataElement( + DataElement.ALTERNATIVE, + [ + DataElement(DataElement.BOOLEAN, True), + DataElement(DataElement.TEXT_STRING, 'hello'), + ], + ) basic_check(e) e = DataElement(DataElement.URL, 'http://example.com') @@ -133,10 +142,14 @@ def test_data_elements(): e = DataElement.boolean(True) basic_check(e) - e = DataElement.sequence([DataElement.signed_integer(0, 1), DataElement.text_string('hello')]) + e = DataElement.sequence( + [DataElement.signed_integer(0, 1), DataElement.text_string('hello')] + ) basic_check(e) - e = DataElement.alternative([DataElement.signed_integer(0, 1), DataElement.text_string('hello')]) + e = DataElement.alternative( + [DataElement.signed_integer(0, 1), DataElement.text_string('hello')] + ) basic_check(e) e = DataElement.url('http://foobar.com') diff --git a/tests/self_test.py b/tests/self_test.py index a1d2ddd..1a1a474 100644 --- a/tests/self_test.py +++ b/tests/self_test.py @@ -22,17 +22,16 @@ import os import pytest from bumble.controller import Controller +from bumble.core import BT_BR_EDR_TRANSPORT, BT_PERIPHERAL_ROLE, BT_CENTRAL_ROLE from bumble.link import LocalLink from bumble.device import Device, Peer from bumble.host import Host from bumble.gatt import Service, Characteristic from bumble.transport import AsyncPipeSink +from bumble.pairing import PairingConfig, PairingDelegate from bumble.smp import ( - PairingConfig, - PairingDelegate, SMP_PAIRING_NOT_SUPPORTED_ERROR, SMP_CONFIRM_VALUE_FAILED_ERROR, - SMP_ID_KEY_DISTRIBUTION_FLAG, ) from bumble.core import ProtocolError @@ -48,20 +47,21 @@ class TwoDevices: def __init__(self): self.connections = [None, None] + addresses = ['F0:F1:F2:F3:F4:F5', 'F5:F4:F3:F2:F1:F0'] self.link = LocalLink() self.controllers = [ - Controller('C1', link = self.link), - Controller('C2', link = self.link) + Controller('C1', link=self.link, public_address=addresses[0]), + Controller('C2', link=self.link, public_address=addresses[1]), ] self.devices = [ Device( - address = 'F0:F1:F2:F3:F4:F5', - host = Host(self.controllers[0], AsyncPipeSink(self.controllers[0])) + address=addresses[0], + host=Host(self.controllers[0], AsyncPipeSink(self.controllers[0])), ), Device( - address = 'F5:F4:F3:F2:F1:F0', - host = Host(self.controllers[1], AsyncPipeSink(self.controllers[1])) - ) + address=addresses[1], + host=Host(self.controllers[1], AsyncPipeSink(self.controllers[1])), + ), ] self.paired = [None, None] @@ -80,8 +80,12 @@ async def test_self_connection(): two_devices = TwoDevices() # Attach listeners - two_devices.devices[0].on('connection', lambda connection: two_devices.on_connection(0, connection)) - two_devices.devices[1].on('connection', lambda connection: two_devices.on_connection(1, connection)) + two_devices.devices[0].on( + 'connection', lambda connection: two_devices.on_connection(0, connection) + ) + two_devices.devices[1].on( + 'connection', lambda connection: two_devices.on_connection(1, connection) + ) # Start await two_devices.devices[0].power_on() @@ -91,8 +95,62 @@ async def test_self_connection(): await two_devices.devices[0].connect(two_devices.devices[1].random_address) # Check the post conditions - assert(two_devices.connections[0] is not None) - assert(two_devices.connections[1] is not None) + assert two_devices.connections[0] is not None + assert two_devices.connections[1] is not None + + +# ----------------------------------------------------------------------------- +@pytest.mark.asyncio +@pytest.mark.parametrize( + 'responder_role,', + (BT_CENTRAL_ROLE, BT_PERIPHERAL_ROLE), +) +async def test_self_classic_connection(responder_role): + # Create two devices, each with a controller, attached to the same link + two_devices = TwoDevices() + + # Attach listeners + two_devices.devices[0].on( + 'connection', lambda connection: two_devices.on_connection(0, connection) + ) + two_devices.devices[1].on( + 'connection', lambda connection: two_devices.on_connection(1, connection) + ) + + # Enable Classic connections + two_devices.devices[0].classic_enabled = True + two_devices.devices[1].classic_enabled = True + + # Start + await two_devices.devices[0].power_on() + await two_devices.devices[1].power_on() + + # Connect the two devices + await asyncio.gather( + two_devices.devices[0].connect( + two_devices.devices[1].public_address, transport=BT_BR_EDR_TRANSPORT + ), + two_devices.devices[1].accept( + two_devices.devices[0].public_address, responder_role + ), + ) + + # Check the post conditions + assert two_devices.connections[0] is not None + assert two_devices.connections[1] is not None + + # Check the role + assert two_devices.connections[0].role != responder_role + assert two_devices.connections[1].role == responder_role + + # Role switch + await two_devices.connections[0].switch_role(responder_role) + + # Check the role + assert two_devices.connections[0].role == responder_role + assert two_devices.connections[1].role != responder_role + + await two_devices.connections[0].disconnect() # ----------------------------------------------------------------------------- @@ -104,63 +162,117 @@ async def test_self_gatt(): # Add some GATT characteristics to device 1 c1 = Characteristic( '3A143AD7-D4A7-436B-97D6-5B62C315E833', - Characteristic.READ, + Characteristic.Properties.READ, Characteristic.READABLE, - bytes([1, 2, 3]) + bytes([1, 2, 3]), ) c2 = Characteristic( '9557CCE2-DB37-46EB-94C4-50AE5B9CB0F8', - Characteristic.READ | Characteristic.WRITE, + Characteristic.Properties.READ | Characteristic.Properties.WRITE, Characteristic.READABLE | Characteristic.WRITEABLE, - bytes([4, 5, 6]) + bytes([4, 5, 6]), ) c3 = Characteristic( '84FC1A2E-C52D-4A2D-B8C3-8855BAB86638', - Characteristic.READ | Characteristic.WRITE_WITHOUT_RESPONSE, + Characteristic.Properties.READ + | Characteristic.Properties.WRITE_WITHOUT_RESPONSE, Characteristic.READABLE | Characteristic.WRITEABLE, - bytes([7, 8, 9]) + bytes([7, 8, 9]), ) c4 = Characteristic( '84FC1A2E-C52D-4A2D-B8C3-8855BAB86638', - Characteristic.READ | Characteristic.NOTIFY | Characteristic.INDICATE, + Characteristic.Properties.READ + | Characteristic.Properties.NOTIFY + | Characteristic.Properties.INDICATE, Characteristic.READABLE, - bytes([1, 1, 1]) + bytes([1, 1, 1]), ) s1 = Service('8140E247-04F0-42C1-BC34-534C344DAFCA', [c1, c2, c3]) s2 = Service('97210A0F-1875-4D05-9E5D-326EB171257A', [c4]) - two_devices.devices[1].add_services([s1, s2]) + s3 = Service('1853', []) + s4 = Service('3A12C182-14E2-4FE0-8C5B-65D7C569F9DB', [], included_services=[s2, s3]) + two_devices.devices[1].add_services([s1, s2, s4]) # Start await two_devices.devices[0].power_on() await two_devices.devices[1].power_on() # Connect the two devices - connection = await two_devices.devices[0].connect(two_devices.devices[1].random_address) + connection = await two_devices.devices[0].connect( + two_devices.devices[1].random_address + ) peer = Peer(connection) bogus_uuid = 'A0AA6007-0B48-4BBE-80AC-0DE9AAF541EA' result = await peer.discover_services([bogus_uuid]) - assert(result == []) + assert result == [] services = peer.get_services_by_uuid(bogus_uuid) - assert(len(services) == 0) + assert len(services) == 0 result = await peer.discover_service(s1.uuid) - assert(len(result) == 1) + assert len(result) == 1 services = peer.get_services_by_uuid(s1.uuid) - assert(len(services) == 1) + assert len(services) == 1 s = services[0] - assert(services[0].uuid == s1.uuid) + assert services[0].uuid == s1.uuid result = await peer.discover_characteristics([c1.uuid], s) - assert(len(result) == 1) + assert len(result) == 1 characteristics = peer.get_characteristics_by_uuid(c1.uuid) - assert(len(characteristics) == 1) + assert len(characteristics) == 1 c = characteristics[0] - assert(c.uuid == c1.uuid) + assert c.uuid == c1.uuid result = await peer.read_value(c) - assert(result is not None) - assert(result == c1.value) + assert result is not None + assert result == c1.value + + result = await peer.discover_service(s4.uuid) + assert len(result) == 1 + result = await peer.discover_included_services(result[0]) + assert len(result) == 2 + # Service UUID is only present when the UUID is 16-bit Bluetooth UUID + assert result[1].uuid.to_bytes() == s3.uuid.to_bytes() + + +# ----------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_self_gatt_long_read(): + # Create two devices, each with a controller, attached to the same link + two_devices = TwoDevices() + + # Add some GATT characteristics to device 1 + characteristics = [ + Characteristic( + f'3A143AD7-D4A7-436B-97D6-5B62C315{i:04X}', + Characteristic.Properties.READ, + Characteristic.READABLE, + bytes([x & 255 for x in range(i)]), + ) + for i in range(0, 513) + ] + + service = Service('8140E247-04F0-42C1-BC34-534C344DAFCA', characteristics) + two_devices.devices[1].add_service(service) + + # Start + await two_devices.devices[0].power_on() + await two_devices.devices[1].power_on() + + # Connect the two devices + connection = await two_devices.devices[0].connect( + two_devices.devices[1].random_address + ) + peer = Peer(connection) + + result = await peer.discover_service(service.uuid) + assert len(result) == 1 + found_service = result[0] + found_characteristics = await found_service.discover_characteristics() + assert len(found_characteristics) == 513 + for i, characteristic in enumerate(found_characteristics): + value = await characteristic.read_value() + assert value == characteristics[i].value # ----------------------------------------------------------------------------- @@ -173,53 +285,85 @@ async def _test_self_smp_with_configs(pairing_config1, pairing_config2): await two_devices.devices[1].power_on() # Attach listeners - two_devices.devices[0].on('connection', lambda connection: two_devices.on_connection(0, connection)) - two_devices.devices[1].on('connection', lambda connection: two_devices.on_connection(1, connection)) + two_devices.devices[0].on( + 'connection', lambda connection: two_devices.on_connection(0, connection) + ) + two_devices.devices[1].on( + 'connection', lambda connection: two_devices.on_connection(1, connection) + ) # Connect the two devices - connection = await two_devices.devices[0].connect(two_devices.devices[1].random_address) - assert(not connection.is_encrypted) + connection = await two_devices.devices[0].connect( + two_devices.devices[1].random_address + ) + assert not connection.is_encrypted # Attach connection listeners - two_devices.connections[0].on('pairing', lambda keys: two_devices.on_paired(0, keys)) - two_devices.connections[1].on('pairing', lambda keys: two_devices.on_paired(1, keys)) + two_devices.connections[0].on( + 'pairing', lambda keys: two_devices.on_paired(0, keys) + ) + two_devices.connections[1].on( + 'pairing', lambda keys: two_devices.on_paired(1, keys) + ) # Set up the pairing configs if pairing_config1: - two_devices.devices[0].pairing_config_factory = lambda connection: pairing_config1 + two_devices.devices[ + 0 + ].pairing_config_factory = lambda connection: pairing_config1 if pairing_config2: - two_devices.devices[1].pairing_config_factory = lambda connection: pairing_config2 + two_devices.devices[ + 1 + ].pairing_config_factory = lambda connection: pairing_config2 # Pair await two_devices.devices[0].pair(connection) - assert(connection.is_encrypted) - assert(two_devices.paired[0] is not None) - assert(two_devices.paired[1] is not None) + assert connection.is_encrypted + assert two_devices.paired[0] is not None + assert two_devices.paired[1] is not None # ----------------------------------------------------------------------------- IO_CAP = [ - PairingDelegate.NO_OUTPUT_NO_INPUT, - PairingDelegate.KEYBOARD_INPUT_ONLY, - PairingDelegate.DISPLAY_OUTPUT_ONLY, - PairingDelegate.DISPLAY_OUTPUT_AND_YES_NO_INPUT, - PairingDelegate.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT + PairingDelegate.IoCapability.NO_OUTPUT_NO_INPUT, + PairingDelegate.IoCapability.KEYBOARD_INPUT_ONLY, + PairingDelegate.IoCapability.DISPLAY_OUTPUT_ONLY, + PairingDelegate.IoCapability.DISPLAY_OUTPUT_AND_YES_NO_INPUT, + PairingDelegate.IoCapability.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT, ] SC = [False, True] MITM = [False, True] # Key distribution is a 4-bit bitmask -# IdKey is necessary for current SMP structure -KEY_DIST = [i for i in range(16) if (i & SMP_ID_KEY_DISTRIBUTION_FLAG)] +KEY_DIST = range(16) + @pytest.mark.asyncio -@pytest.mark.parametrize('io_cap, sc, mitm, key_dist', - itertools.product(IO_CAP, SC, MITM, KEY_DIST) +@pytest.mark.parametrize( + 'io_caps, sc, mitm, key_dist', + itertools.chain( + itertools.product([IO_CAP], SC, MITM, [15]), + itertools.product( + [[PairingDelegate.IoCapability.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT]], + SC, + MITM, + KEY_DIST, + ), + ), ) -async def test_self_smp(io_cap, sc, mitm, key_dist): +async def test_self_smp(io_caps, sc, mitm, key_dist): class Delegate(PairingDelegate): - def __init__(self, name, io_capability, local_initiator_key_distribution, local_responder_key_distribution): - super().__init__(io_capability, local_initiator_key_distribution, - local_responder_key_distribution) + def __init__( + self, + name, + io_capability, + local_initiator_key_distribution, + local_responder_key_distribution, + ): + super().__init__( + io_capability, + local_initiator_key_distribution, + local_responder_key_distribution, + ) self.name = name self.reset() @@ -227,9 +371,10 @@ async def test_self_smp(io_cap, sc, mitm, key_dist): self.peer_delegate = None self.number = asyncio.get_running_loop().create_future() + # pylint: disable-next=unused-argument async def compare_numbers(self, number, digits): if self.peer_delegate is None: - logger.warn(f'[{self.name}] no peer delegate') + logger.warning(f'[{self.name}] no peer delegate') return False await self.display_number(number, digits=6) logger.debug(f'[{self.name}] waiting for peer number') @@ -239,10 +384,13 @@ async def test_self_smp(io_cap, sc, mitm, key_dist): async def get_number(self): if self.peer_delegate is None: - logger.warn(f'[{self.name}] no peer delegate') + logger.warning(f'[{self.name}] no peer delegate') return 0 else: - if self.peer_delegate.io_capability == PairingDelegate.KEYBOARD_INPUT_ONLY: + if ( + self.peer_delegate.io_capability + == PairingDelegate.IoCapability.KEYBOARD_INPUT_ONLY + ): peer_number = 6789 else: logger.debug(f'[{self.name}] waiting for peer number') @@ -259,12 +407,15 @@ async def test_self_smp(io_cap, sc, mitm, key_dist): pairing_config_sets = [('Initiator', [None]), ('Responder', [None])] for pairing_config_set in pairing_config_sets: - delegate = Delegate(pairing_config_set[0], io_cap, key_dist, key_dist) - pairing_config_set[1].append(PairingConfig(sc, mitm, True, delegate)) + for io_cap in io_caps: + delegate = Delegate(pairing_config_set[0], io_cap, key_dist, key_dist) + pairing_config_set[1].append(PairingConfig(sc, mitm, True, delegate)) for pairing_config1 in pairing_config_sets[0][1]: for pairing_config2 in pairing_config_sets[1][1]: - logger.info(f'########## self_smp with {pairing_config1} and {pairing_config2}') + logger.info( + f'########## self_smp with {pairing_config1} and {pairing_config2}' + ) if pairing_config1: pairing_config1.delegate.reset() if pairing_config2: @@ -274,7 +425,6 @@ async def test_self_smp(io_cap, sc, mitm, key_dist): pairing_config2.delegate.peer_delegate = pairing_config1.delegate await _test_self_smp_with_configs(pairing_config1, pairing_config2) - # ----------------------------------------------------------------------------- @@ -282,20 +432,20 @@ async def test_self_smp(io_cap, sc, mitm, key_dist): async def test_self_smp_reject(): class RejectingDelegate(PairingDelegate): def __init__(self): - super().__init__(PairingDelegate.NO_OUTPUT_NO_INPUT) + super().__init__(PairingDelegate.IoCapability.NO_OUTPUT_NO_INPUT) async def accept(self): return False - rejecting_pairing_config = PairingConfig(delegate = RejectingDelegate()) + rejecting_pairing_config = PairingConfig(delegate=RejectingDelegate()) paired = False try: await _test_self_smp_with_configs(None, rejecting_pairing_config) paired = True except ProtocolError as error: - assert(error.error_code == SMP_PAIRING_NOT_SUPPORTED_ERROR) + assert error.error_code == SMP_PAIRING_NOT_SUPPORTED_ERROR - assert(not paired) + assert not paired # ----------------------------------------------------------------------------- @@ -303,31 +453,37 @@ async def test_self_smp_reject(): async def test_self_smp_wrong_pin(): class WrongPinDelegate(PairingDelegate): def __init__(self): - super().__init__(PairingDelegate.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT) + super().__init__( + PairingDelegate.IoCapability.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT + ) async def compare_numbers(self, number, digits): return False - wrong_pin_pairing_config = PairingConfig(delegate = WrongPinDelegate()) + wrong_pin_pairing_config = PairingConfig(mitm=True, delegate=WrongPinDelegate()) paired = False try: - await _test_self_smp_with_configs(wrong_pin_pairing_config, wrong_pin_pairing_config) + await _test_self_smp_with_configs( + wrong_pin_pairing_config, wrong_pin_pairing_config + ) paired = True except ProtocolError as error: - assert(error.error_code == SMP_CONFIRM_VALUE_FAILED_ERROR) + assert error.error_code == SMP_CONFIRM_VALUE_FAILED_ERROR - assert(not paired) + assert not paired # ----------------------------------------------------------------------------- async def run_test_self(): await test_self_connection() await test_self_gatt() + await test_self_gatt_long_read() await test_self_smp() await test_self_smp_reject() await test_self_smp_wrong_pin() + # ----------------------------------------------------------------------------- if __name__ == '__main__': - logging.basicConfig(level = os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) + logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'INFO').upper()) asyncio.run(run_test_self()) diff --git a/tests/smp_test.py b/tests/smp_test.py index 9120c47..bdfa021 100644 --- a/tests/smp_test.py +++ b/tests/smp_test.py @@ -15,12 +15,16 @@ # ----------------------------------------------------------------------------- # Imports # ----------------------------------------------------------------------------- -from bumble.crypto import * +from bumble.crypto import EccKey, aes_cmac, ah, c1, f4, f5, f6, g2, h6, h7, s1 # ----------------------------------------------------------------------------- -def reversed_hex(hex): - return bytes(reversed(bytes.fromhex(hex))) +# pylint: disable=invalid-name +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +def reversed_hex(hex_str): + return bytes(reversed(bytes.fromhex(hex_str))) # ----------------------------------------------------------------------------- @@ -29,39 +33,41 @@ def test_ecc(): x = key.x y = key.y - assert(len(x) == 32) - assert(len(y) == 32) + assert len(x) == 32 + assert len(y) == 32 # Test DH with test vectors from the spec - private_A = '3f49f6d4 a3c55f38 74c9b3e3 d2103f50 4aff607b eb40b799 5899b8a6 cd3c1abd' - private_B = '55188b3d 32f6bb9a 900afcfb eed4e72a 59cb9ac2 f19d7cfb 6b4fdd49 f47fc5fd' - public_A_x = '20b003d2 f297be2c 5e2c83a7 e9f9a5b9 eff49111 acf4fddb cc030148 0e359de6' - public_A_y = 'dc809c49 652aeb6d 63329abf 5a52155c 766345c2 8fed3024 741c8ed0 1589d28b' - public_B_x = '1ea1f0f0 1faf1d96 09592284 f19e4c00 47b58afd 8615a69f 559077b2 2faaa190' - public_B_y = '4c55f33e 429dad37 7356703a 9ab85160 472d1130 e28e3676 5f89aff9 15b1214a' - dhkey = 'ec0234a3 57c8ad05 341010a6 0a397d9b 99796b13 b4f866f1 868d34f3 73bfa698' + private_A = ( + '3f49f6d4 a3c55f38 74c9b3e3 d2103f50 4aff607b eb40b799 5899b8a6 cd3c1abd' + ) + private_B = ( + '55188b3d 32f6bb9a 900afcfb eed4e72a 59cb9ac2 f19d7cfb 6b4fdd49 f47fc5fd' + ) + public_A_x = ( + '20b003d2 f297be2c 5e2c83a7 e9f9a5b9 eff49111 acf4fddb cc030148 0e359de6' + ) + public_A_y = ( + 'dc809c49 652aeb6d 63329abf 5a52155c 766345c2 8fed3024 741c8ed0 1589d28b' + ) + public_B_x = ( + '1ea1f0f0 1faf1d96 09592284 f19e4c00 47b58afd 8615a69f 559077b2 2faaa190' + ) + public_B_y = ( + '4c55f33e 429dad37 7356703a 9ab85160 472d1130 e28e3676 5f89aff9 15b1214a' + ) + dhkey = 'ec0234a3 57c8ad05 341010a6 0a397d9b 99796b13 b4f866f1 868d34f3 73bfa698' key_a = EccKey.from_private_key_bytes( - bytes.fromhex(private_A), - bytes.fromhex(public_A_x), - bytes.fromhex(public_A_y) - ) - shared_key = key_a.dh( - bytes.fromhex(public_B_x), - bytes.fromhex(public_B_y) + bytes.fromhex(private_A), bytes.fromhex(public_A_x), bytes.fromhex(public_A_y) ) - assert(shared_key == bytes.fromhex(dhkey)) + shared_key = key_a.dh(bytes.fromhex(public_B_x), bytes.fromhex(public_B_y)) + assert shared_key == bytes.fromhex(dhkey) key_b = EccKey.from_private_key_bytes( - bytes.fromhex(private_B), - bytes.fromhex(public_B_x), - bytes.fromhex(public_B_y) + bytes.fromhex(private_B), bytes.fromhex(public_B_x), bytes.fromhex(public_B_y) ) - shared_key = key_b.dh( - bytes.fromhex(public_A_x), - bytes.fromhex(public_A_y) - ) - assert(shared_key == bytes.fromhex(dhkey)) + shared_key = key_b.dh(bytes.fromhex(public_A_x), bytes.fromhex(public_A_y)) + assert shared_key == bytes.fromhex(dhkey) # ----------------------------------------------------------------------------- @@ -75,7 +81,7 @@ def test_c1(): rat = 0 ra = reversed_hex('B1B2B3B4B5B6') result = c1(k, r, preq, pres, iat, rat, ia, ra) - assert(result == reversed_hex('1e1e3fef878988ead2a74dc5bef13b86')) + assert result == reversed_hex('1e1e3fef878988ead2a74dc5bef13b86') # ----------------------------------------------------------------------------- @@ -84,7 +90,7 @@ def test_s1(): r1 = reversed_hex('000F0E0D0C0B0A091122334455667788') r2 = reversed_hex('010203040506070899AABBCCDDEEFF00') result = s1(k, r1, r2) - assert(result == reversed_hex('9a1fe1f0e8b0f49b5b4216ae796da062')) + assert result == reversed_hex('9a1fe1f0e8b0f49b5b4216ae796da062') # ----------------------------------------------------------------------------- @@ -92,59 +98,77 @@ def test_aes_cmac(): m = b'' k = bytes.fromhex('2b7e1516 28aed2a6 abf71588 09cf4f3c') cmac = aes_cmac(m, k) - assert(cmac == bytes.fromhex('bb1d6929 e9593728 7fa37d12 9b756746')) + assert cmac == bytes.fromhex('bb1d6929 e9593728 7fa37d12 9b756746') m = bytes.fromhex('6bc1bee2 2e409f96 e93d7e11 7393172a') cmac = aes_cmac(m, k) - assert(cmac == bytes.fromhex('070a16b4 6b4d4144 f79bdd9d d04a287c')) + assert cmac == bytes.fromhex('070a16b4 6b4d4144 f79bdd9d d04a287c') m = bytes.fromhex( - '6bc1bee2 2e409f96 e93d7e11 7393172a' + - 'ae2d8a57 1e03ac9c 9eb76fac 45af8e51' + - '30c81c46 a35ce411' + '6bc1bee2 2e409f96 e93d7e11 7393172a' + + 'ae2d8a57 1e03ac9c 9eb76fac 45af8e51' + + '30c81c46 a35ce411' ) cmac = aes_cmac(m, k) - assert(cmac == bytes.fromhex('dfa66747 de9ae630 30ca3261 1497c827')) + assert cmac == bytes.fromhex('dfa66747 de9ae630 30ca3261 1497c827') m = bytes.fromhex( - '6bc1bee2 2e409f96 e93d7e11 7393172a' + - 'ae2d8a57 1e03ac9c 9eb76fac 45af8e51' + - '30c81c46 a35ce411 e5fbc119 1a0a52ef' + - 'f69f2445 df4f9b17 ad2b417b e66c3710' + '6bc1bee2 2e409f96 e93d7e11 7393172a' + + 'ae2d8a57 1e03ac9c 9eb76fac 45af8e51' + + '30c81c46 a35ce411 e5fbc119 1a0a52ef' + + 'f69f2445 df4f9b17 ad2b417b e66c3710' ) cmac = aes_cmac(m, k) - assert(cmac == bytes.fromhex('51f0bebf 7e3b9d92 fc497417 79363cfe')) + assert cmac == bytes.fromhex('51f0bebf 7e3b9d92 fc497417 79363cfe') # ----------------------------------------------------------------------------- def test_f4(): - u = bytes(reversed(bytes.fromhex( - '20b003d2 f297be2c 5e2c83a7 e9f9a5b9' + - 'eff49111 acf4fddb cc030148 0e359de6' - ))) - v = bytes(reversed(bytes.fromhex( - '55188b3d 32f6bb9a 900afcfb eed4e72a' + - '59cb9ac2 f19d7cfb 6b4fdd49 f47fc5fd' - ))) + u = bytes( + reversed( + bytes.fromhex( + '20b003d2 f297be2c 5e2c83a7 e9f9a5b9' + + 'eff49111 acf4fddb cc030148 0e359de6' + ) + ) + ) + v = bytes( + reversed( + bytes.fromhex( + '55188b3d 32f6bb9a 900afcfb eed4e72a' + + '59cb9ac2 f19d7cfb 6b4fdd49 f47fc5fd' + ) + ) + ) x = bytes(reversed(bytes.fromhex('d5cb8454 d177733e ffffb2ec 712baeab'))) z = bytes([0]) value = f4(u, v, x, z) - assert(bytes(reversed(value)) == bytes.fromhex('f2c916f1 07a9bd1c f1eda1be a974872d')) + assert bytes(reversed(value)) == bytes.fromhex( + 'f2c916f1 07a9bd1c f1eda1be a974872d' + ) # ----------------------------------------------------------------------------- def test_f5(): - w = bytes(reversed(bytes.fromhex( - 'ec0234a3 57c8ad05 341010a6 0a397d9b' + - '99796b13 b4f866f1 868d34f3 73bfa698' - ))) + w = bytes( + reversed( + bytes.fromhex( + 'ec0234a3 57c8ad05 341010a6 0a397d9b' + + '99796b13 b4f866f1 868d34f3 73bfa698' + ) + ) + ) n1 = bytes(reversed(bytes.fromhex('d5cb8454 d177733e ffffb2ec 712baeab'))) n2 = bytes(reversed(bytes.fromhex('a6e8e7cc 25a75f6e 216583f7 ff3dc4cf'))) a1 = bytes(reversed(bytes.fromhex('00561237 37bfce'))) a2 = bytes(reversed(bytes.fromhex('00a71370 2dcfc1'))) value = f5(w, n1, n2, a1, a2) - assert(bytes(reversed(value[0])) == bytes.fromhex('2965f176 a1084a02 fd3f6a20 ce636e20')) - assert(bytes(reversed(value[1])) == bytes.fromhex('69867911 69d7cd23 980522b5 94750a38')) + assert bytes(reversed(value[0])) == bytes.fromhex( + '2965f176 a1084a02 fd3f6a20 ce636e20' + ) + assert bytes(reversed(value[1])) == bytes.fromhex( + '69867911 69d7cd23 980522b5 94750a38' + ) # ----------------------------------------------------------------------------- @@ -157,37 +181,47 @@ def test_f6(): a1 = bytes(reversed(bytes.fromhex('00561237 37bfce'))) a2 = bytes(reversed(bytes.fromhex('00a71370 2dcfc1'))) value = f6(mac_key, n1, n2, r, io_cap, a1, a2) - assert(bytes(reversed(value)) == bytes.fromhex('e3c47398 9cd0e8c5 d26c0b09 da958f61')) + assert bytes(reversed(value)) == bytes.fromhex( + 'e3c47398 9cd0e8c5 d26c0b09 da958f61' + ) # ----------------------------------------------------------------------------- def test_g2(): - u = bytes(reversed(bytes.fromhex( - '20b003d2 f297be2c 5e2c83a7 e9f9a5b9' + - 'eff49111 acf4fddb cc030148 0e359de6' - ))) - v = bytes(reversed(bytes.fromhex( - '55188b3d 32f6bb9a 900afcfb eed4e72a' + - '59cb9ac2 f19d7cfb 6b4fdd49 f47fc5fd' - ))) + u = bytes( + reversed( + bytes.fromhex( + '20b003d2 f297be2c 5e2c83a7 e9f9a5b9' + + 'eff49111 acf4fddb cc030148 0e359de6' + ) + ) + ) + v = bytes( + reversed( + bytes.fromhex( + '55188b3d 32f6bb9a 900afcfb eed4e72a' + + '59cb9ac2 f19d7cfb 6b4fdd49 f47fc5fd' + ) + ) + ) x = bytes(reversed(bytes.fromhex('d5cb8454 d177733e ffffb2ec 712baeab'))) y = bytes(reversed(bytes.fromhex('a6e8e7cc 25a75f6e 216583f7 ff3dc4cf'))) value = g2(u, v, x, y) - assert(value == 0x2f9ed5ba) + assert value == 0x2F9ED5BA # ----------------------------------------------------------------------------- def test_h6(): KEY = bytes.fromhex('ec0234a3 57c8ad05 341010a6 0a397d9b') KEY_ID = bytes.fromhex('6c656272') - assert(h6(KEY, KEY_ID) == bytes.fromhex('2d9ae102 e76dc91c e8d3a9e2 80b16399')) + assert h6(KEY, KEY_ID) == bytes.fromhex('2d9ae102 e76dc91c e8d3a9e2 80b16399') # ----------------------------------------------------------------------------- def test_h7(): KEY = bytes.fromhex('ec0234a3 57c8ad05 341010a6 0a397d9b') SALT = bytes.fromhex('00000000 00000000 00000000 746D7031') - assert(h7(SALT, KEY) == bytes.fromhex('fb173597 c6a3c0ec d2998c2a 75a57011')) + assert h7(SALT, KEY) == bytes.fromhex('fb173597 c6a3c0ec d2998c2a 75a57011') # ----------------------------------------------------------------------------- @@ -196,7 +230,7 @@ def test_ah(): prand = bytes(reversed(bytes.fromhex('708194'))) value = ah(irk, prand) expected = bytes(reversed(bytes.fromhex('0dfbaa'))) - assert(value == expected) + assert value == expected # ----------------------------------------------------------------------------- diff --git a/tests/transport_test.py b/tests/transport_test.py index 3005345..cd3c5f2 100644 --- a/tests/transport_test.py +++ b/tests/transport_test.py @@ -37,7 +37,9 @@ def test_parser(): parser2 = PacketParser(sink2) for parser in [parser1, parser2]: - with open(os.path.join(os.path.dirname(__file__), 'hci_data_001.bin'), 'rb') as input: + with open( + os.path.join(os.path.dirname(__file__), 'hci_data_001.bin'), 'rb' + ) as input: while True: n = random.randint(1, 9) data = input.read(n) @@ -45,7 +47,7 @@ def test_parser(): break parser.feed_data(data) - assert(sink1.packets == sink2.packets) + assert sink1.packets == sink2.packets # ----------------------------------------------------------------------------- @@ -60,15 +62,16 @@ def test_parser_extensions(): except ValueError: exception_thrown = True - assert(exception_thrown) + assert exception_thrown # Now add a custom info parser.extended_packet_info[0x77] = (1, 1, 'B') parser.reset() parser.feed_data(bytes([0x77, 0x00, 0x02, 0x01, 0x02])) - assert(len(sink.packets) == 1) + assert len(sink.packets) == 1 # ----------------------------------------------------------------------------- -test_parser() -test_parser_extensions()
\ No newline at end of file +if __name__ == '__main__': + test_parser() + test_parser_extensions() diff --git a/web/index.html b/web/index.html index c8c77e0..4374db0 100644 --- a/web/index.html +++ b/web/index.html @@ -128,4 +128,4 @@ } </script> </body> -</html>
\ No newline at end of file +</html> diff --git a/web/scanner.py b/web/scanner.py index 9ab9f47..59eda67 100644 --- a/web/scanner.py +++ b/web/scanner.py @@ -16,14 +16,16 @@ # Imports # ----------------------------------------------------------------------------- from bumble.device import Device -from bumble.transport import PacketParser +from bumble.transport.common import PacketParser # ----------------------------------------------------------------------------- class ScannerListener(Device.Listener): - def on_advertisement(self, address, ad_data, rssi, connectable): - address_type_string = ('P', 'R', 'PI', 'RI')[address.address_type] - print(f'>>> {address} [{address_type_string}]: RSSI={rssi}, {ad_data}') + def on_advertisement(self, advertisement): + address_type_string = ('P', 'R', 'PI', 'RI')[advertisement.address.address_type] + print( + f'>>> {advertisement.address} [{address_type_string}]: RSSI={advertisement.rssi}, {advertisement.ad_data}' + ) class HciSource: |