Repo: Organise and document processes and architecture (#43)
* Add Makefile for common contributor tasks Narrows down a few commands to automatically format when building, and neatly expose testing/benching. Empty files added to describe contributor guidelines, overall architecture. * First draft of contributor guidelines * Simple architecture diagrams * Add PNG variants of architecture diagrams Swapping to these because not having Fira Sans installed on a viewing machine leads to terrible kerning. * Architecture description. * MD cross-refs.
This commit is contained in:
92
ARCHITECTURE.md
Normal file
92
ARCHITECTURE.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# Summary
|
||||
Songbird defines two main systems:
|
||||
* The **gateway**, which communicates with Discord through another client library. This sends voice state updates to join a voice channel, and correlates responses into voice connection info.
|
||||
* The **driver**, which uses voice connection info to establish an RTP connection and WS signalling channel to send and receive audio. It then manages audio mixing, audio source management, event tracking, and voice packet reception.
|
||||
|
||||
Songbird allows users to use one or both of these systems as needed.
|
||||
Discord voice connections ultimately require both of these to be handled in some way.
|
||||
In many setups for instance, this comes through using a client-specific wrapper in a bot's host language to collect connection information to send to Lavalink/Lavaplayer, hosted on the JVM.
|
||||
|
||||
# Gateway
|
||||
Songbird's **gateway** is an async system, typically managed by a top-level `Songbird` struct, which holds `Arc` pointers to a Discord client library instance (that it can request individual shard references from).
|
||||
This maps all `ChannelID`s into `Call` state.
|
||||
New `Call`s are created as needed, requesting the shard that each `ChannelID` belongs to from the main client.
|
||||
|
||||
When asked to join a voice channel, a `Call` communicates with Discord over the shard handle, collates Discord's responses, and produces a `ConnectionInfo` with session information.
|
||||
If the driver feature is enabled, then every `Call` is/has an associated `Driver`, and this connection info is passed on to its inner tasks.
|
||||
|
||||

|
||||
|
||||
```
|
||||
src/manager.rs
|
||||
src/handler.rs
|
||||
src/serenity.rs
|
||||
```
|
||||
|
||||
# Driver
|
||||
Songbird's **driver** is a mixed sync/async system for running voice connections.
|
||||
Audio processing remains synchronous for the following reasons:
|
||||
* Encryption, encoding, and mixing are compute bound tasks which cannot be subdivided cleanly by the Tokio executor. Having these block the scheduler's finite thread count has a significant impact on servicing other tasks.
|
||||
* `Read` and `Seek` are considerably more user-friendly to use, implement, and integrate than `AsyncRead`, `AsyncBufRead`, and `AsyncSeek`.
|
||||
|
||||
## Tasks
|
||||
Songbird subdivides voice connection handling into several long- and short-lived tasks.
|
||||
|
||||
* **Core**: Handles and directs commands received from the driver. Responsible for connection/reconnection, and creates network tasks.
|
||||
* **Mixer**: Combines audio sources together, Opus encodes the result, and encrypts the built packets every 20ms. Responsible for handling track commands/state. ***Synchronous***.
|
||||
* **Disposer**: Used by mixer thread to dispose of data with potentially long/blocking `Drop` implementations (i.e., audio sources). ***Synchronous***.
|
||||
* **Events**: Stores and runs event handlers, tracks event timing, and handles
|
||||
* **Websocket**: *Network task.* Sends speaking status updates and keepalives to Discord, and receives client (dis)connect events.
|
||||
* **UDP Tx**: *Network task.* Responsible for transmitting completed voice packets.
|
||||
* **UDP Rx**: *Network task.* Decrypts/decodes received voice packets and statistics information.
|
||||
|
||||
*Note: all tasks are able to message the permanent tasks via a block of interconnecting channels.*
|
||||
|
||||

|
||||
|
||||
```
|
||||
src/driver/*
|
||||
```
|
||||
|
||||
## Audio handling
|
||||
|
||||
### Input
|
||||
Inputs are raw audio sources: composed of a `Reader` (which can be `Read`-only or `Read + Seek`), a framing mechanism, and a codec.
|
||||
Several wrappers exist to add `Seek` capabilities to one-way streams via storage or explicitly recreating the struct.
|
||||
|
||||
Framing is not always needed (`Raw`), but makes it possible to consume the correct number of bytes needed to decode one audio packet (and/or simplify skipping through the stream).
|
||||
Currently, Opus and raw (`i16`/`f32`) audio sources are supported, though only the DCA framing for Opus is implemented.
|
||||
At present, the use of the FFmpeg executable allows us to receive raw input, but at heavy memory cost.
|
||||
Further implementations are possible in the present framework (e.g., WebM/MKV and Ogg containers, MP3 and linked FFI FFmpeg as codecs).
|
||||
|
||||
Internally, the mixer uses floating-point audio to prevent clipping and allow more granular volume control.
|
||||
If a source is known to use the Opus codec (and is the only source), then it can bypass mixing altogether.
|
||||
|
||||
```
|
||||
src/input/*
|
||||
```
|
||||
|
||||
### Tracks
|
||||
Tracks hold additional state which is expected to change over the lifetime of a track: position, play state, and modifiers like volume.
|
||||
Tracks (and their handles) also allow per-source events to be inserted.
|
||||
|
||||
Tracks are defined in user code, where they are fully modifiable, before being passed into the driver.
|
||||
From this point, all changes and requests are serviced via commands over a `TrackHandle` (so that the audio thread never locks or blocks during user modification).
|
||||
|
||||
Tracks and Inputs typically exist in a 1:1 relationship, though many Inputs may reference the same backing store.
|
||||
|
||||
```
|
||||
src/tracks/*
|
||||
```
|
||||
|
||||
## Events
|
||||
Event handlers are stored on a per-track and global basis, with events being supplied by other tasks in the driver.
|
||||
These event handlers are boxed trait objects, each subscribed to an individual event type.
|
||||
The event type and data are supplied when this generic handler is called, allowing reuse of event handlers between subscriptions (i.e., via `Arc`).
|
||||
|
||||
Timed events are driven by "tick" messages sent by the mixer (so that both tasks' view of track state remains in sync), while other event types are set individually (but often fired in batches).
|
||||
Global events fire in response to other tasks, or the main "tick".
|
||||
|
||||
```
|
||||
src/events/*
|
||||
```
|
||||
80
CONTRIBUTING.md
Normal file
80
CONTRIBUTING.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Issues
|
||||
Thanks for raising a bug or feature request!
|
||||
If you're raising a bug or issue, please use the following template:
|
||||
|
||||
```md
|
||||
**Songbird version:** (version)
|
||||
|
||||
**Rust version (`rustc -V`):** (version)
|
||||
|
||||
**Serenity/Twilight version:** (version)
|
||||
|
||||
**Output of `ffmpeg -version`, `youtube-dl --version` (if relevant):**
|
||||
...
|
||||
|
||||
**Description:**
|
||||
...
|
||||
|
||||
**Steps to reproduce:**
|
||||
...
|
||||
```
|
||||
|
||||
Additionally, tag your issue at the same time you create it.
|
||||
|
||||
If you're requesting a feature, explain how it will be useful to users or improve the library.
|
||||
If you want to implement it yourself, please include a rough explanation of *how* you'll be going about writing it.
|
||||
|
||||
# Pull Requests
|
||||
Thanks for considering adding new features or fixing bugs in Songbird!
|
||||
You might find it helpful to look over [our architecture document] in this repository to understand roughly how components interact at a high level.
|
||||
Generally, we ask that PRs have a description that answers the following, under headers or in prose:
|
||||
|
||||
* The type of change being made.
|
||||
* A high-level description of the changes.
|
||||
* Steps taken to test the new feature/fix.
|
||||
|
||||
Your PR should also readily compile, pass all tests, and undergo automated formatting *before* it is opened.
|
||||
The simplest way to check that your PR is ready is to install [cargo make], and run the following command:
|
||||
```sh
|
||||
cargo make ready
|
||||
```
|
||||
|
||||
Merged PRs will be squashed into the repository under a single headline: try to tag your PR correctly, and title it with a single short sentence in the imperative mood to make your work easier to merge.
|
||||
*"Driver: Fix missing track state events"* is a good example: it explains what code was modified, the problem that was solved, and would place the description of *how* the problem was solved in the commit/PR body.
|
||||
|
||||
If you're adding new features or utilities, please open an issue and/or speak with us on Discord to make sure that you aren't duplicating work, and are in line with the overall system architecture.
|
||||
|
||||
At a high level, focus on making new features as clean and usable as possible.
|
||||
This extends to directing users away from footguns and functions with surprising effects, at the API level or by documentation.
|
||||
Changes that affect or invalidate large areas of the library API will make a lot of users' lives that much harder when new breaking releases are published, so need deeper justification.
|
||||
Focus on making sure that new feature additions are general to as many use-cases as possible: for instance, adding some queue-specific state to every audio track forces other users to pay for that additional overhead even when they aren't using this feature.
|
||||
|
||||
## Breaking changes
|
||||
Breaking changes (in API or API semantics) must be made to target the `"next"` branch.
|
||||
Commits here will be released in the next breaking semantic version (i.e., 0.1.7 -> 0.2.0, 1.3.2 -> 2.0.0).
|
||||
|
||||
Bugfixes and new features which do not break semantic versioning should target `"current"`.
|
||||
Patches will be folded into more incremental patch updates (i.e., 1.3.2 -> 1.3.3) while new features will trigger minor updates (i.e., 1.3.2 -> 1.4.0).
|
||||
|
||||
## Documentation and naming
|
||||
Doc-comments, comments, and item names should be written in British English where possible.
|
||||
All items (`structs`, `enums`, `fn`s, etc.) must be documented in full sentences; these are user-facing, and other developers will naturally rely on them to write correct code and understand what the library can(not) do for them.
|
||||
Error conditions, reasons to prefer one method over another, and potential use risks should be explained to help library users write the best code they can.
|
||||
|
||||
Code comments should be written similarly – this requirement is not as stringent, but focus on clarity and conciseness.
|
||||
Try to focus on explaining *why/what* more confusing code exists/does, rather than *how* it performs that task, to try to prevent comments from aging as the codebase evolves.
|
||||
|
||||
## Testing
|
||||
Pull requests must not break existing tests, examples, or common feature subsets.
|
||||
Where possible, new features should include new tests (particularly around event or input handling).
|
||||
|
||||
These steps are included in `cargo make ready`.
|
||||
|
||||
## Linting
|
||||
Songbird's linting pipeline requires that you have nightly Rust installed.
|
||||
Your code must be formatted using `cargo +nightly fmt --all`, and must not add any more Clippy warnings than the base repository already has (as extra lints are added to Clippy over time).
|
||||
|
||||
These commands are included in `cargo make ready`.
|
||||
|
||||
[cargo make]: https://github.com/sagiegurari/cargo-make
|
||||
[our architecture document]: ARCHITECTURE.md
|
||||
51
Makefile.toml
Normal file
51
Makefile.toml
Normal file
@@ -0,0 +1,51 @@
|
||||
[tasks.format]
|
||||
toolchain = "nightly"
|
||||
install_crate = { crate_name = "rustfmt-nightly", rustup_component_name = "rustfmt-preview", binary = "rustfmt", test_arg = "--help" }
|
||||
command = "cargo"
|
||||
args = ["fmt", "--all"]
|
||||
|
||||
[tasks.build]
|
||||
args = ["build", "--features", "full-doc"]
|
||||
dependencies = ["format"]
|
||||
|
||||
[tasks.build-examples]
|
||||
args = ["build", "--manifest-path", "./examples/Cargo.toml", "--workspace"]
|
||||
command = "cargo"
|
||||
dependencies = ["format"]
|
||||
|
||||
[tasks.build-gateway]
|
||||
args = ["build", "--features", "serenity-rustls"]
|
||||
command = "cargo"
|
||||
dependencies = ["format"]
|
||||
|
||||
[tasks.build-driver]
|
||||
args = ["build", "--features", "driver,rustls"]
|
||||
command = "cargo"
|
||||
dependencies = ["format"]
|
||||
|
||||
[tasks.build-old-tokio]
|
||||
command = "cargo"
|
||||
args = ["build", "--features", "serenity-rustls-tokio-02,driver-tokio-02"]
|
||||
dependencies = ["format"]
|
||||
|
||||
[tasks.build-variants]
|
||||
dependencies = ["build", "build-gateway", "build-driver", "build-old-tokio"]
|
||||
|
||||
[tasks.clippy]
|
||||
args = ["clippy", "--features", "full-doc", "--", "-D", "warnings"]
|
||||
dependencies = ["format"]
|
||||
|
||||
[tasks.test]
|
||||
args = ["test", "--features", "full-doc"]
|
||||
|
||||
[tasks.bench]
|
||||
description = "Runs performance benchmarks."
|
||||
category = "Test"
|
||||
command = "cargo"
|
||||
args = ["bench", "--features", "internals,full-doc"]
|
||||
|
||||
[tasks.doc]
|
||||
args = ["doc", "--features", "full-doc"]
|
||||
|
||||
[tasks.ready]
|
||||
dependencies = ["format", "test", "build-variants", "build-examples", "doc", "clippy"]
|
||||
@@ -20,7 +20,6 @@ The library offers:
|
||||
Songbird's gateway functionality requires you to specify the `GUILD_VOICE_STATES` intent.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Songbird needs a few system dependencies before you can use it.
|
||||
|
||||
- Opus - Audio codec that Discord uses.
|
||||
@@ -45,8 +44,10 @@ This is an optional dependency. It allows Songbird to download an audio source f
|
||||
## Examples
|
||||
Full examples showing various types of functionality and integrations can be found in [this crate's examples directory].
|
||||
|
||||
## Attribution
|
||||
## Contributing
|
||||
If you want to help out or file an issue, please look over [our contributor guidelines]!
|
||||
|
||||
## Attribution
|
||||
Songbird's logo is based upon the copyright-free image ["Black-Capped Chickadee"] by George Gorgas White.
|
||||
|
||||
[serenity]: https://github.com/serenity-rs/serenity
|
||||
@@ -54,6 +55,7 @@ Songbird's logo is based upon the copyright-free image ["Black-Capped Chickadee"
|
||||
["Black-Capped Chickadee"]: https://www.oldbookillustrations.com/illustrations/black-capped-chickadee/
|
||||
[lavalink]: https://github.com/Frederikam/Lavalink
|
||||
[this crate's examples directory]: https://github.com/serenity-rs/songbird/tree/current/examples
|
||||
[our contributor guidelines]: CONTRIBUTING.md
|
||||
|
||||
[build badge]: https://img.shields.io/github/workflow/status/serenity-rs/songbird/CI?style=flat-square
|
||||
[build]: https://github.com/serenity-rs/songbird/actions
|
||||
|
||||
BIN
images/arch.afdesign
Normal file
BIN
images/arch.afdesign
Normal file
Binary file not shown.
BIN
images/driver.png
Normal file
BIN
images/driver.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
136
images/driver.svg
Normal file
136
images/driver.svg
Normal file
@@ -0,0 +1,136 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 1525 1013" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||
<g transform="matrix(1,0,0,1,-31,-921)">
|
||||
<g transform="matrix(1,0,0,1,0,900)">
|
||||
<g transform="matrix(1,0,0,1,299,-52.947)">
|
||||
<path d="M212.72,146.182L212.72,132.063L521.426,132.063L521.426,103.825L540.487,139.123L521.426,174.421L521.426,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-168.926,12.6064)">
|
||||
<text x="235.411px" y="98.782px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:84.043px;">M<tspan x="301.889px " y="98.782px ">e</tspan>ssag<tspan x="515.19px " y="98.782px ">e</tspan>s</text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,995)">
|
||||
<g transform="matrix(1,0,0,1,299,-52.947)">
|
||||
<path d="M212.72,146.182L212.72,132.063L521.426,132.063L521.426,103.825L540.487,139.123L521.426,174.421L521.426,146.182L212.72,146.182Z" style="fill:rgb(255,77,74);"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-168.926,12.6064)">
|
||||
<text x="235.411px" y="98.782px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:84.043px;">Cr<tspan x="314.076px " y="98.782px ">e</tspan>at<tspan x="435.517px " y="98.782px ">e</tspan>s</text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,-15)">
|
||||
<g transform="matrix(1,0,0,1,-168.926,1110.98)">
|
||||
<text x="235.411px" y="98.782px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:84.043px;">P<tspan x="282.559px " y="98.782px ">e</tspan>rmanent</text>
|
||||
</g>
|
||||
<g transform="matrix(1.75342,0,0,0.430769,335.83,655.838)">
|
||||
<rect x="101.876" y="1143.29" width="184.054" height="163.884" style="fill:rgb(255,193,74);stroke:rgb(62,62,62);stroke-width:3.05px;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-1507.73,1026.16)">
|
||||
<path d="M1841.87,401.515C1861.04,401.559 1876.53,417.076 1876.53,436.182L1876.53,505.518C1876.53,524.651 1861,540.185 1841.87,540.185L1632.2,540.185L1632.2,401.515L1841.87,401.515Z" style="fill:rgb(255,73,27);stroke:rgb(255,5,5);stroke-width:4.07px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-326.049,1208.17)">
|
||||
<text x="503.152px" y="302.395px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;fill:white;">Driv<tspan x="592.852px " y="302.395px ">e</tspan>r</text>
|
||||
</g>
|
||||
<g transform="matrix(0.960543,0,0,1,84.2035,0)">
|
||||
<path d="M89.27,1672.76L1407.47,1672.76" style="fill:none;stroke:rgb(62,62,62);stroke-width:3.97px;stroke-dasharray:39.71,39.71,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-260,37)">
|
||||
<text x="409.473px" y="1589.56px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">As<tspan x="460.973px " y="1589.56px ">y</tspan>nc</text>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-260,153.713)">
|
||||
<text x="409.473px" y="1589.56px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">S<tspan x="435.723px " y="1589.56px ">y</tspan>nc</text>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-257.171,22.6916)">
|
||||
<g transform="matrix(0.724138,0,0,0.724138,223.721,413.46)">
|
||||
<ellipse cx="829.267" cy="1498.79" rx="134.889" ry="73.117" style="fill:rgb(255,193,74);stroke:rgb(62,62,62);stroke-width:5.38px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,10.0851,37.8193)">
|
||||
<text x="762.453px" y="1473.58px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">C<tspan x="789.953px 819.153px 837.953px " y="1473.58px 1473.58px 1473.58px ">ore</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-47.9044,-158.841)">
|
||||
<g transform="matrix(0.724138,0,0,0.724138,223.721,413.46)">
|
||||
<ellipse cx="829.267" cy="1498.79" rx="134.889" ry="73.117" style="fill:rgb(255,193,74);stroke:rgb(62,62,62);stroke-width:5.38px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-12.6064,37.8193)">
|
||||
<text x="762.453px" y="1473.58px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Ev<tspan x="813.303px " y="1473.58px ">e</tspan>nts</text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,264.735)">
|
||||
<g transform="matrix(0.724138,0,0,0.724138,223.721,413.46)">
|
||||
<ellipse cx="829.267" cy="1498.79" rx="134.889" ry="73.117" style="fill:rgb(255,193,74);stroke:rgb(62,62,62);stroke-width:5.38px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,4.54747e-13,40.3406)">
|
||||
<text x="762.453px" y="1473.58px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Mix<tspan x="839.603px " y="1473.58px ">e</tspan>r</text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-320.203,332.81)">
|
||||
<g transform="matrix(0.924892,0,0,0.924892,52.9138,113.02)">
|
||||
<ellipse cx="829.267" cy="1498.79" rx="134.889" ry="73.117" style="fill:rgb(255,193,74);stroke:rgb(62,62,62);stroke-width:4.21px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-3,40.3406)">
|
||||
<text x="725.078px" y="1473.58px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Disposer</text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(0.234011,0.0406263,-0.10801,0.622149,635.72,1439.47)">
|
||||
<path d="M212.72,146.182L212.72,132.063L489.811,132.063L489.811,103.825L540.487,139.123L489.811,174.421L489.811,146.182L212.72,146.182Z" style="fill:rgb(255,77,74);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.942841,-0.272086,0.175081,0.606697,445.215,1481.7)">
|
||||
<path d="M212.72,146.182L212.72,132.063L528.222,132.063L528.222,103.825L540.487,139.123L528.222,174.421L528.222,146.182L212.72,146.182Z" style="fill:rgb(255,77,74);"/>
|
||||
</g>
|
||||
<g transform="matrix(1.39485,-2.12445e-16,-9.76394e-16,0.631455,377.024,1435.17)">
|
||||
<path d="M212.72,146.182L212.72,132.063L531.858,132.063L531.858,103.825L540.487,139.123L531.858,174.421L531.858,146.182L212.72,146.182Z" style="fill:rgb(255,77,74);"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,57.9896,47.9044)">
|
||||
<g transform="matrix(1.01835,0,0,1.01835,-11.9813,-27.0542)">
|
||||
<ellipse cx="829.267" cy="1498.79" rx="134.889" ry="73.117" style="fill:white;stroke:rgb(62,62,62);stroke-width:3.82px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,9,40.3406)">
|
||||
<text x="702.378px" y="1473.58px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">W<tspan x="742.178px " y="1473.58px ">e</tspan>bsock<tspan x="900.928px " y="1473.58px ">e</tspan>t</text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,284.905,-123.543)">
|
||||
<g transform="matrix(1.01835,0,0,1.01835,-11.9813,-27.0542)">
|
||||
<ellipse cx="829.267" cy="1498.79" rx="134.889" ry="73.117" style="fill:white;stroke:rgb(62,62,62);stroke-width:3.82px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,9,40.3406)">
|
||||
<text x="743.378px" y="1473.58px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">UDP Rx</text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,438.704,63.0321)">
|
||||
<g transform="matrix(1.01835,0,0,1.01835,-11.9813,-27.0542)">
|
||||
<ellipse cx="829.267" cy="1498.79" rx="134.889" ry="73.117" style="fill:white;stroke:rgb(62,62,62);stroke-width:3.82px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,9,40.3406)">
|
||||
<text x="747.078px" y="1473.58px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">UDP T<tspan x="877.528px " y="1473.58px ">x</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(-0.266822,0.110146,-0.240947,-0.583678,811.395,1831.03)">
|
||||
<path d="M212.72,146.182L212.72,132.063L498.791,132.063L498.791,103.825L540.487,139.123L498.791,174.421L498.791,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.675184,-0.370199,0.303584,0.553689,739.208,1743.48)">
|
||||
<path d="M212.72,146.182L212.72,132.063L524.856,132.063L524.856,103.825L540.487,139.123L524.856,174.421L524.856,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.0764172,-0.232117,0.599787,0.197461,745.524,1728.62)">
|
||||
<path d="M212.72,146.182L212.72,132.063L491.234,132.063L491.234,103.825L540.487,139.123L491.234,174.421L491.234,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(-0.09184,-0.226458,0.585165,-0.237313,776.07,1552.03)">
|
||||
<path d="M212.72,146.182L212.72,132.063L491.234,132.063L491.234,103.825L540.487,139.123L491.234,174.421L491.234,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(-0.242347,-0.0314007,0.0811391,-0.62622,1007.44,1453.94)">
|
||||
<path d="M212.72,146.182L212.72,132.063L491.234,132.063L491.234,103.825L540.487,139.123L491.234,174.421L491.234,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.331589,0.464216,-0.513833,0.36703,645.231,1416.91)">
|
||||
<path d="M251.791,146.182L251.791,174.421L212.72,139.123L251.791,103.825L251.791,132.063L519.389,132.063L519.389,103.825L540.487,139.123L519.389,174.421L519.389,146.182L251.791,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(-0.238092,0.276193,-0.478275,-0.412296,812.148,1376.42)">
|
||||
<path d="M246.95,146.182L246.95,174.421L212.72,139.123L246.95,103.825L246.95,132.063L507.48,132.063L507.48,103.825L540.487,139.123L507.48,174.421L507.48,146.182L246.95,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(0.268623,0.0378223,-0.0880408,0.625287,331.337,1404.06)">
|
||||
<path d="M212.72,146.182L212.72,132.063L496.118,132.063L496.118,103.825L540.487,139.123L496.118,174.421L496.118,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
BIN
images/gateway.png
Normal file
BIN
images/gateway.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
215
images/gateway.svg
Normal file
215
images/gateway.svg
Normal file
@@ -0,0 +1,215 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 1753 824" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||
<g transform="matrix(1.10366,0,0,1,-18.3017,0)">
|
||||
<g transform="matrix(0.737132,0,0,0.846154,46.4114,33.4555)">
|
||||
<path d="M781.666,258.432C781.666,235.819 762.572,217.461 739.053,217.461L219.171,217.461C195.652,217.461 176.558,235.819 176.558,258.432L176.558,340.374C176.558,362.986 195.652,381.344 219.171,381.344L739.053,381.344C762.572,381.344 781.666,362.986 781.666,340.374L781.666,258.432Z" style="fill:rgb(255,228,192);stroke:rgb(35,35,35);stroke-width:1.2px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.783133,0,0,0.783133,68.5968,62.5)">
|
||||
<path d="M658.753,217.461C677.931,217.505 693.421,233.022 693.421,252.129L693.421,321.464C693.421,340.598 677.887,356.132 658.753,356.132L431.838,356.132L431.838,217.461L658.753,217.461Z" style="fill:rgb(255,127,0);fill-opacity:0.48;stroke:rgb(198,79,0);stroke-width:4.94px;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.906077,0,0,1,-8.99696,2.84839)">
|
||||
<text x="466.127px" y="302.395px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">HashMap</text>
|
||||
</g>
|
||||
<g transform="matrix(0.906077,0,0,1,-221.731,3)">
|
||||
<text x="469.277px" y="302.395px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Songbir<tspan x="643.627px " y="302.395px ">d</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1,0,0,1,-451.31,-216.831)">
|
||||
<g transform="matrix(0.854545,0,0,0.854545,237.148,123.1)">
|
||||
<g transform="matrix(1,0,0,1,0,-98.3301)">
|
||||
<path d="M1841.87,401.515C1861.04,401.559 1876.53,417.076 1876.53,436.182L1876.53,505.518C1876.53,524.651 1861,540.185 1841.87,540.185L1632.2,540.185L1632.2,401.515L1841.87,401.515Z" style="fill:rgb(255,73,27);fill-opacity:0.33;stroke:rgb(255,5,5);stroke-width:4.77px;stroke-dasharray:23.84,23.84,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.813542,0,0,0.846154,1240.62,119.179)">
|
||||
<path d="M219.171,217.461L481.336,217.461L481.336,381.344L219.171,381.344C195.652,381.344 176.558,362.986 176.558,340.374L176.558,258.432C176.558,235.851 195.598,217.513 219.171,217.461Z" style="fill:rgb(255,228,192);stroke:rgb(35,35,35);stroke-width:1.41px;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(0.945455,0,0,0.945455,88.9306,55.5373)">
|
||||
<g transform="matrix(1,0,0,1,0,-98.3301)">
|
||||
<path d="M1841.87,401.515C1861.04,401.559 1876.53,417.076 1876.53,436.182L1876.53,505.518C1876.53,524.651 1861,540.185 1841.87,540.185L1632.2,540.185L1632.2,401.515L1841.87,401.515Z" style="fill:rgb(255,73,27);fill-opacity:0.33;stroke:rgb(255,5,5);stroke-width:4.31px;stroke-dasharray:21.55,21.55,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.813542,0,0,0.846154,1240.62,119.179)">
|
||||
<path d="M219.171,217.461L481.336,217.461L481.336,381.344L219.171,381.344C195.652,381.344 176.558,362.986 176.558,340.374L176.558,258.432C176.558,235.851 195.598,217.513 219.171,217.461Z" style="fill:rgb(255,228,192);stroke:rgb(35,35,35);stroke-width:1.27px;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1,0,0,1,0,-98.3301)">
|
||||
<path d="M1841.87,401.515C1861.04,401.559 1876.53,417.076 1876.53,436.182L1876.53,505.518C1876.53,524.651 1861,540.185 1841.87,540.185L1632.2,540.185L1632.2,401.515L1841.87,401.515Z" style="fill:rgb(255,73,27);fill-opacity:0.33;stroke:rgb(255,5,5);stroke-width:4.07px;stroke-dasharray:20.37,20.37,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.813542,0,0,0.846154,1240.62,119.179)">
|
||||
<path d="M219.171,217.461L481.336,217.461L481.336,381.344L219.171,381.344C195.652,381.344 176.558,362.986 176.558,340.374L176.558,258.432C176.558,235.851 195.598,217.513 219.171,217.461Z" style="fill:rgb(255,228,192);stroke:rgb(35,35,35);stroke-width:1.2px;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1,0,0,1,944.679,88.7237)">
|
||||
<text x="529.152px" y="302.395px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Call</text>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,1184.2,88.7237)">
|
||||
<text x="503.152px" y="302.395px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Driv<tspan x="592.852px " y="302.395px ">e</tspan>r</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(0.0946177,0.344866,-0.619948,0.170089,1136.16,128.848)">
|
||||
<path d="M212.72,146.182L212.72,132.063L506.222,132.063L506.222,103.825L540.487,139.123L506.222,174.421L506.222,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.0663822,0.241952,-0.619948,0.170089,1188.64,184.303)">
|
||||
<path d="M212.72,146.182L212.72,132.063L491.648,132.063L491.648,103.825L540.487,139.123L491.648,174.421L491.648,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.0421663,0.153689,-0.619948,0.170089,1237.68,231.478)">
|
||||
<path d="M212.72,146.182L212.72,132.063L463.6,132.063L463.6,103.825L540.487,139.123L463.6,174.421L463.6,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1,0,0,1,-29.6907,0)">
|
||||
<path d="M1310.14,364.726C1310.14,355.642 1302.77,348.267 1293.68,348.267L1126.51,348.267C1117.43,348.267 1110.05,355.642 1110.05,364.726L1110.05,397.643C1110.05,406.727 1117.43,414.102 1126.51,414.102L1293.68,414.102C1302.77,414.102 1310.14,406.727 1310.14,397.643L1310.14,364.726Z" style="fill:rgb(139,161,237);stroke:rgb(62,62,62);stroke-width:3.89px;stroke-dasharray:19.47,19.47,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,650.273,106.466)">
|
||||
<text x="462.743px" y="286.536px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:37.5px;fill:white;">Shar<tspan x="539.656px " y="286.536px ">d</tspan> 1</text>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1,0,0,1,-116.988,80.6811)">
|
||||
<text x="1571.47px" y="151.889px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:150px;">}</text>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-151.277,77.613)">
|
||||
<text x="1682.41px" y="119.616px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Shar<tspan x="1784.96px " y="119.616px ">d</tspan> 1</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1,0,0,1,-451.31,156.32)">
|
||||
<g transform="matrix(0.854545,0,0,0.854545,237.148,123.1)">
|
||||
<g transform="matrix(1,0,0,1,0,-98.3301)">
|
||||
<path d="M1841.87,401.515C1861.04,401.559 1876.53,417.076 1876.53,436.182L1876.53,505.518C1876.53,524.651 1861,540.185 1841.87,540.185L1632.2,540.185L1632.2,401.515L1841.87,401.515Z" style="fill:rgb(255,73,27);fill-opacity:0.33;stroke:rgb(255,5,5);stroke-width:4.77px;stroke-dasharray:23.84,23.84,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.813542,0,0,0.846154,1240.62,119.179)">
|
||||
<path d="M219.171,217.461L481.336,217.461L481.336,381.344L219.171,381.344C195.652,381.344 176.558,362.986 176.558,340.374L176.558,258.432C176.558,235.851 195.598,217.513 219.171,217.461Z" style="fill:rgb(255,228,192);stroke:rgb(35,35,35);stroke-width:1.41px;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(0.945455,0,0,0.945455,88.9306,55.5373)">
|
||||
<g transform="matrix(1,0,0,1,0,-98.3301)">
|
||||
<path d="M1841.87,401.515C1861.04,401.559 1876.53,417.076 1876.53,436.182L1876.53,505.518C1876.53,524.651 1861,540.185 1841.87,540.185L1632.2,540.185L1632.2,401.515L1841.87,401.515Z" style="fill:rgb(255,73,27);fill-opacity:0.33;stroke:rgb(255,5,5);stroke-width:4.31px;stroke-dasharray:21.55,21.55,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.813542,0,0,0.846154,1240.62,119.179)">
|
||||
<path d="M219.171,217.461L481.336,217.461L481.336,381.344L219.171,381.344C195.652,381.344 176.558,362.986 176.558,340.374L176.558,258.432C176.558,235.851 195.598,217.513 219.171,217.461Z" style="fill:rgb(255,228,192);stroke:rgb(35,35,35);stroke-width:1.27px;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(0.813542,0,0,0.846154,1240.62,119.179)">
|
||||
<path d="M219.171,217.461L481.336,217.461L481.336,381.344L219.171,381.344C195.652,381.344 176.558,362.986 176.558,340.374L176.558,258.432C176.558,235.851 195.598,217.513 219.171,217.461Z" style="fill:rgb(255,228,192);stroke:rgb(35,35,35);stroke-width:1.2px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,-98.3301)">
|
||||
<path d="M1841.87,401.515C1861.04,401.559 1876.53,417.076 1876.53,436.182L1876.53,505.518C1876.53,524.651 1861,540.185 1841.87,540.185L1632.2,540.185L1632.2,401.515L1841.87,401.515Z" style="fill:rgb(255,73,27);fill-opacity:0.33;stroke:rgb(255,5,5);stroke-width:4.07px;stroke-dasharray:20.37,20.37,0,0;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1,0,0,1,944.679,88.7237)">
|
||||
<text x="529.152px" y="302.395px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Call</text>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,1184.2,88.7237)">
|
||||
<text x="503.152px" y="302.395px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Driv<tspan x="592.852px " y="302.395px ">e</tspan>r</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,374.218)">
|
||||
<g transform="matrix(0.0946177,0.344866,-0.619948,0.170089,1136.16,128.848)">
|
||||
<path d="M212.72,146.182L212.72,132.063L506.222,132.063L506.222,103.825L540.487,139.123L506.222,174.421L506.222,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.0663822,0.241952,-0.619948,0.170089,1188.64,184.303)">
|
||||
<path d="M212.72,146.182L212.72,132.063L491.648,132.063L491.648,103.825L540.487,139.123L491.648,174.421L491.648,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.0421663,0.153689,-0.619948,0.170089,1237.68,231.478)">
|
||||
<path d="M212.72,146.182L212.72,132.063L463.6,132.063L463.6,103.825L540.487,139.123L463.6,174.421L463.6,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,371.779)">
|
||||
<g transform="matrix(1,0,0,1,-29.6907,0)">
|
||||
<path d="M1310.14,364.726C1310.14,355.642 1302.77,348.267 1293.68,348.267L1126.51,348.267C1117.43,348.267 1110.05,355.642 1110.05,364.726L1110.05,397.643C1110.05,406.727 1117.43,414.102 1126.51,414.102L1293.68,414.102C1302.77,414.102 1310.14,406.727 1310.14,397.643L1310.14,364.726Z" style="fill:rgb(139,161,237);stroke:rgb(62,62,62);stroke-width:3.89px;stroke-dasharray:19.47,19.47,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,650.273,106.466)">
|
||||
<text x="459.987px" y="286.536px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:37.5px;fill:white;">Shar<tspan x="536.899px " y="286.536px ">d</tspan></text>
|
||||
<text x="569.262px" y="286.536px" style="font-family:'FiraSans-Italic', 'Fira Sans', sans-serif;font-style:italic;font-size:37.5px;fill:white;">n</text>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,0,383.235)">
|
||||
<g transform="matrix(1,0,0,1,-116.988,80.6811)">
|
||||
<text x="1571.47px" y="151.889px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:150px;">}</text>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-151.277,77.613)">
|
||||
<text x="1682.41px" y="119.616px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">Shar<tspan x="1784.96px " y="119.616px ">d</tspan></text>
|
||||
<text x="1828.11px" y="119.616px" style="font-family:'FiraSans-Italic', 'Fira Sans', sans-serif;font-style:italic;font-size:50px;">n</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(0.741053,0,0,0.741053,48.1674,-91.4668)">
|
||||
<g transform="matrix(1,0,0,1,6,0)">
|
||||
<g transform="matrix(0.901042,0,0,2.63462,20.9267,170.853)">
|
||||
<path d="M781.666,258.432C781.666,235.819 727.987,217.461 661.869,217.461L296.355,217.461C230.237,217.461 176.558,235.819 176.558,258.432L176.558,340.374C176.558,362.986 230.237,381.344 296.355,381.344L661.869,381.344C727.987,381.344 781.666,362.986 781.666,340.374L781.666,258.432Z" style="fill:rgb(114,137,218);stroke:rgb(35,35,35);stroke-width:0.69px;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-3.15161,-12.6064)">
|
||||
<path d="M728.089,1048.33L728.089,1080.21C728.089,1139.76 679.765,1188.11 620.146,1188.16L438.141,1188.16L438.141,1048.33L728.089,1048.33Z" style="fill:rgb(139,161,237);stroke:white;stroke-width:8.51px;stroke-dasharray:42.54,42.54,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-3.15161,-12.6064)">
|
||||
<path d="M438.141,897.056L438.141,756.386L620.146,756.386C679.765,756.429 728.089,804.78 728.089,864.328L728.089,897.056L438.141,897.056Z" style="fill:rgb(139,161,237);stroke:white;stroke-width:8.51px;stroke-dasharray:42.54,42.54,0,0;"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-838.328,27.2129)">
|
||||
<rect x="1273.32" y="857.237" width="289.948" height="151.277" style="fill:rgb(139,161,237);stroke:white;stroke-width:8.51px;stroke-dasharray:42.54,42.54,0,0;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1.13666,0,0,1.13666,-312.867,612.333)">
|
||||
<text x="460.172px" y="283.325px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;fill:white;">Disc<tspan x="553.222px 582.422px 601.222px " y="283.325px 283.325px 283.325px ">ord</tspan></text>
|
||||
<text x="479.847px" y="333.325px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;fill:white;">Client</text>
|
||||
<text x="466.347px" y="383.325px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;fill:white;">Libr<tspan x="553.347px 580.547px 600.347px " y="383.325px 383.325px 383.325px ">ary</tspan></text>
|
||||
</g>
|
||||
<g transform="matrix(1.13666,0,0,1.13666,-48.1323,498.875)">
|
||||
<text x="461.972px" y="297.848px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;fill:white;">Shar<tspan x="564.522px " y="297.848px ">d</tspan> 1</text>
|
||||
</g>
|
||||
<g transform="matrix(1.13666,0,0,1.13666,-48.1323,779.368)">
|
||||
<text x="458.297px" y="297.848px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;fill:white;">Shar<tspan x="560.847px " y="297.848px ">d</tspan></text>
|
||||
<text x="603.997px" y="297.848px" style="font-family:'FiraSans-Italic', 'Fira Sans', sans-serif;font-style:italic;font-size:50px;fill:white;">n</text>
|
||||
</g>
|
||||
<g transform="matrix(1.13666,0,0,1.13666,-48.1323,640.697)">
|
||||
<text x="527.647px" y="297.848px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;fill:white;">...</text>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1,0,0,1,0,-52.947)">
|
||||
<path d="M212.72,146.182L212.72,132.063L521.426,132.063L521.426,103.825L540.487,139.123L521.426,174.421L521.426,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-168.926,12.6064)">
|
||||
<text x="235.411px" y="98.782px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:84.043px;">Ar<tspan x="315.168px " y="98.782px ">c</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g transform="matrix(1.83697e-17,0.3,-0.642857,3.93636e-17,408.05,294.658)">
|
||||
<path d="M212.72,146.182L212.72,132.063L499.642,132.063L499.642,103.825L540.487,139.123L499.642,174.421L499.642,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.779783,-0.223329,0.176998,0.618011,465.48,201.157)">
|
||||
<path d="M212.72,146.182L212.72,132.063L525.38,132.063L525.38,103.825L540.487,139.123L525.38,174.421L525.38,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.828668,-0.0314876,0.0244096,0.642394,476.658,156.682)">
|
||||
<path d="M212.72,146.182L212.72,132.063L525.711,132.063L525.711,103.825L540.487,139.123L525.711,174.421L525.711,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.855686,0.0684039,-0.0512268,0.640813,481.831,141.267)">
|
||||
<path d="M212.72,146.182L212.72,132.063L526.213,132.063L526.213,103.825L540.487,139.123L526.213,174.421L526.213,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.795457,0.57078,-0.374781,0.522307,539.513,139.939)">
|
||||
<path d="M212.72,146.182L212.72,132.063L527.971,132.063L527.971,103.825L540.487,139.123L527.971,174.421L527.971,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.822319,0.712033,-0.42081,0.485988,540.984,114.14)">
|
||||
<path d="M212.72,146.182L212.72,132.063L529.222,132.063L529.222,103.825L540.487,139.123L529.222,174.421L529.222,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
<g transform="matrix(0.832467,0.849435,-0.459131,0.44996,542.525,90.4338)">
|
||||
<path d="M212.72,146.182L212.72,132.063L530.184,132.063L530.184,103.825L540.487,139.123L530.184,174.421L530.184,146.182L212.72,146.182Z" style="fill:rgb(62,62,62);"/>
|
||||
</g>
|
||||
</g>
|
||||
<g transform="matrix(1,0,0,1,-73.5812,-20.6544)">
|
||||
<text x="939.817px" y="336.649px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">.</text>
|
||||
<text x="939.817px" y="386.649px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">.</text>
|
||||
<text x="939.817px" y="436.649px" style="font-family:'FiraSans-Regular', 'Fira Sans', sans-serif;font-size:50px;">.</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
Reference in New Issue
Block a user