From 78da43f51db3e0c05b2cbde732775bd905552703 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 2 Dec 2020 21:18:53 +0100 Subject: [PATCH] Added simulation functionality --- include/Component.hpp | 14 ++- include/Window.hpp | 11 +++ include/components/ANDgate.hpp | 14 +++ include/components/Lamp.hpp | 24 ++++++ include/components/NANDgate.hpp | 14 +++ include/components/NORgate.hpp | 14 +++ include/components/NOTgate.hpp | 14 +++ include/components/ORgate.hpp | 14 +++ include/components/Switch.hpp | 26 ++++++ include/components/XNORgate.hpp | 14 +++ include/components/XORgate.hpp | 14 +++ src/Component.cpp | 15 ++++ src/Window.cpp | 148 ++++++++++++++++++++++++++++---- ui/Window.ui | 62 +++++++++++++ ui/assets/cursor.png | Bin 0 -> 5083 bytes ui/assets/lamp.png | Bin 0 -> 711 bytes ui/assets/lamp_off.png | Bin 0 -> 8948 bytes ui/assets/lamp_on.png | Bin 0 -> 9101 bytes ui/assets/resources.qrc | 8 ++ ui/assets/switch.png | Bin 0 -> 2190 bytes ui/assets/switch_off.png | Bin 0 -> 8920 bytes ui/assets/switch_on.png | Bin 0 -> 9087 bytes ui/assets/wire.png | Bin 0 -> 789 bytes 23 files changed, 385 insertions(+), 21 deletions(-) create mode 100644 include/components/ANDgate.hpp create mode 100644 include/components/Lamp.hpp create mode 100644 include/components/NANDgate.hpp create mode 100644 include/components/NORgate.hpp create mode 100644 include/components/NOTgate.hpp create mode 100644 include/components/ORgate.hpp create mode 100644 include/components/Switch.hpp create mode 100644 include/components/XNORgate.hpp create mode 100644 include/components/XORgate.hpp create mode 100644 ui/assets/cursor.png create mode 100644 ui/assets/lamp.png create mode 100644 ui/assets/lamp_off.png create mode 100644 ui/assets/lamp_on.png create mode 100644 ui/assets/switch.png create mode 100644 ui/assets/switch_off.png create mode 100644 ui/assets/switch_on.png create mode 100644 ui/assets/wire.png diff --git a/include/Component.hpp b/include/Component.hpp index 70b7b6f..d98e7bc 100644 --- a/include/Component.hpp +++ b/include/Component.hpp @@ -12,8 +12,18 @@ public: explicit Component(QWidget* parent); explicit Component(QWidget* parent, const QString& resource); - void mouseMoveEvent(QMouseEvent* event) override; + virtual void mouseMoveEvent(QMouseEvent* event) override; -private: + QPoint CenterPos(); + void Connect(Component* component); + const QString& Type(); + + virtual bool Action() = 0; + + +protected: Ui::Component* ui; + + QString type; + std::vector connections; }; \ No newline at end of file diff --git a/include/Window.hpp b/include/Window.hpp index ab06d71..29f8e22 100644 --- a/include/Window.hpp +++ b/include/Window.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace Ui { class Window; @@ -20,12 +21,14 @@ protected: void mousePressEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; + void paintEvent(QPaintEvent* event) override; private: void ToggleSimulation(); void ToggleComponentPlacer(); void LoadGhostLabel(const QString& resource); Component* componentAt(const QPoint& point); + Component* createComponent(const QPoint& position); Ui::Window* ui; bool simulating; @@ -34,10 +37,18 @@ private: Component* component; QPoint relativePos; } dragInfo; + bool mouseDown; GhostLabel* ghostImage; QString resourcePath; + QPen pen; + QActionGroup* componentGroup; + enum class eComponent { + NOT, AND, NAND, OR, NOR, XOR, XNOR, SWITCH, LAMP + } componentType; + std::vector components; + std::vector wires; }; \ No newline at end of file diff --git a/include/components/ANDgate.hpp b/include/components/ANDgate.hpp new file mode 100644 index 0000000..cc50293 --- /dev/null +++ b/include/components/ANDgate.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../Component.hpp" + +class ANDGate : public Component +{ +public: + ANDGate(QWidget* parent) : Component(parent, ":/components/and.png") { type = "and"; } + + bool Action() override { + return (connections[0]->Action() && connections[1]->Action()); + } +}; \ No newline at end of file diff --git a/include/components/Lamp.hpp b/include/components/Lamp.hpp new file mode 100644 index 0000000..d30d309 --- /dev/null +++ b/include/components/Lamp.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "../Component.hpp" + +class Lamp : public Component +{ +public: + Lamp(QWidget* parent) : Component(parent, ":/components/lamp_off.png") { type = "lamp"; } + + bool Action() override { + return connections[0]->Action(); + } + + void Turn(bool state) { + if (state) + ui->label->setPixmap(QPixmap(":/components/lamp_on.png")); + else + ui->label->setPixmap(QPixmap(":/components/lamp_off.png")); + } + +private: + bool state = false; +}; \ No newline at end of file diff --git a/include/components/NANDgate.hpp b/include/components/NANDgate.hpp new file mode 100644 index 0000000..7e7cb68 --- /dev/null +++ b/include/components/NANDgate.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../Component.hpp" + +class NANDGate : public Component +{ +public: + NANDGate(QWidget* parent) : Component(parent, ":/components/nand.png") { type = "nand"; } + + bool Action() override { + return !(connections[0]->Action() && connections[1]->Action()); + } +}; \ No newline at end of file diff --git a/include/components/NORgate.hpp b/include/components/NORgate.hpp new file mode 100644 index 0000000..18d9240 --- /dev/null +++ b/include/components/NORgate.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../Component.hpp" + +class NORGate : public Component +{ +public: + NORGate(QWidget* parent) : Component(parent, ":/components/nor.png") { type = "nor"; } + + bool Action() override { + return !(connections[0]->Action() || connections[1]->Action()); + } +}; \ No newline at end of file diff --git a/include/components/NOTgate.hpp b/include/components/NOTgate.hpp new file mode 100644 index 0000000..d8ce9d1 --- /dev/null +++ b/include/components/NOTgate.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../Component.hpp" + +class NOTGate : public Component +{ +public: + NOTGate(QWidget* parent) : Component(parent, ":/components/not.png") { type = "not"; } + + bool Action() override { + return !connections[0]->Action(); + } +}; \ No newline at end of file diff --git a/include/components/ORgate.hpp b/include/components/ORgate.hpp new file mode 100644 index 0000000..7146ac7 --- /dev/null +++ b/include/components/ORgate.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../Component.hpp" + +class ORGate : public Component +{ +public: + ORGate(QWidget* parent) : Component(parent, ":/components/or.png") { type = "or"; } + + bool Action() override { + return (connections[0]->Action() || connections[1]->Action()); + } +}; \ No newline at end of file diff --git a/include/components/Switch.hpp b/include/components/Switch.hpp new file mode 100644 index 0000000..a392f26 --- /dev/null +++ b/include/components/Switch.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "ui_Component.h" +#include "../Component.hpp" + +class Switch : public Component +{ +public: + Switch(QWidget* parent) : Component(parent, ":/components/switch_off.png") { type = "switch"; } + + bool Action() override { + return state; + } + + void Toggle() { + state = !state; + if (state) + ui->label->setPixmap(QPixmap(":/components/switch_on.png")); + else + ui->label->setPixmap(QPixmap(":/components/switch_off.png")); + } + +private: + bool state = false; +}; \ No newline at end of file diff --git a/include/components/XNORgate.hpp b/include/components/XNORgate.hpp new file mode 100644 index 0000000..484125c --- /dev/null +++ b/include/components/XNORgate.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../Component.hpp" + +class XNORGate : public Component +{ +public: + XNORGate(QWidget* parent) : Component(parent, ":/components/xnor.png") { type = "xnor"; } + + bool Action() override { + return !((connections[0]->Action() || connections[1]->Action()) && !(connections[0]->Action() && connections[1]->Action())); + } +}; \ No newline at end of file diff --git a/include/components/XORgate.hpp b/include/components/XORgate.hpp new file mode 100644 index 0000000..dd2d5c9 --- /dev/null +++ b/include/components/XORgate.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "../Component.hpp" + +class XORGate : public Component +{ +public: + XORGate(QWidget* parent) : Component(parent, ":/components/xor.png") { type = "xor"; } + + bool Action() override { + return ((connections[0]->Action() || connections[1]->Action()) && !(connections[0]->Action() && connections[1]->Action())); + } +}; \ No newline at end of file diff --git a/src/Component.cpp b/src/Component.cpp index 575b555..9d39cc3 100644 --- a/src/Component.cpp +++ b/src/Component.cpp @@ -23,3 +23,18 @@ void Component::mouseMoveEvent(QMouseEvent* event) { event->ignore(); } + +QPoint Component::CenterPos() +{ + return pos() + QPoint(size().width() / 2, size().height() / 2); +} + +void Component::Connect(Component* component) +{ + connections.push_back(component); +} + +const QString& Component::Type() +{ + return type; +} diff --git a/src/Window.cpp b/src/Window.cpp index f33bb90..e41a0d2 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -2,6 +2,15 @@ #include "ui_Window.h" #include "Component.hpp" +#include "components/NOTgate.hpp" +#include "components/ANDgate.hpp" +#include "components/NANDgate.hpp" +#include "components/ORgate.hpp" +#include "components/NORgate.hpp" +#include "components/XORgate.hpp" +#include "components/XNORgate.hpp" +#include "components/Switch.hpp" +#include "components/Lamp.hpp" #include #include @@ -21,6 +30,8 @@ Window::Window() : setMouseTracking(true); componentGroup = new QActionGroup(this); + componentGroup->addAction(ui->actionCursor); + componentGroup->addAction(ui->actionWiring); componentGroup->addAction(ui->actionAddNOT); componentGroup->addAction(ui->actionAddAND); componentGroup->addAction(ui->actionAddNAND); @@ -28,16 +39,25 @@ Window::Window() : componentGroup->addAction(ui->actionAddNOR); componentGroup->addAction(ui->actionAddXOR); componentGroup->addAction(ui->actionAddXNOR); - componentGroup->setExclusionPolicy(QActionGroup::ExclusionPolicy::ExclusiveOptional); + componentGroup->addAction(ui->actionAddSwitch); + componentGroup->addAction(ui->actionAddLamp); + componentGroup->setExclusionPolicy(QActionGroup::ExclusionPolicy::Exclusive); connect(ui->actionStart, &QAction::triggered, this, &Window::ToggleSimulation); - connect(ui->actionAddNOT, &QAction::triggered, this, [this]() {resourcePath = ":/components/not.png"; ToggleComponentPlacer(); }); - connect(ui->actionAddAND, &QAction::triggered, this, [this]() {resourcePath = ":/components/and.png"; ToggleComponentPlacer(); }); - connect(ui->actionAddNAND, &QAction::triggered, this, [this]() {resourcePath = ":/components/nand.png"; ToggleComponentPlacer(); }); - connect(ui->actionAddOR, &QAction::triggered, this, [this]() {resourcePath = ":/components/or.png"; ToggleComponentPlacer(); }); - connect(ui->actionAddNOR, &QAction::triggered, this, [this]() {resourcePath = ":/components/nor.png"; ToggleComponentPlacer(); }); - connect(ui->actionAddXOR, &QAction::triggered, this, [this]() {resourcePath = ":/components/xor.png"; ToggleComponentPlacer(); }); - connect(ui->actionAddXNOR, &QAction::triggered, this, [this]() {resourcePath = ":/components/xnor.png"; ToggleComponentPlacer(); }); + connect(ui->actionCursor, &QAction::triggered, this, &Window::ToggleComponentPlacer); + connect(ui->actionWiring, &QAction::triggered, this, &Window::ToggleComponentPlacer); + connect(ui->actionAddNOT, &QAction::triggered, this, [this]() {componentType = eComponent::NOT , resourcePath = ":/components/not.png"; ToggleComponentPlacer(); }); + connect(ui->actionAddAND, &QAction::triggered, this, [this]() {componentType = eComponent::AND, resourcePath = ":/components/and.png"; ToggleComponentPlacer(); }); + connect(ui->actionAddNAND, &QAction::triggered, this, [this]() {componentType = eComponent::NAND , resourcePath = ":/components/nand.png"; ToggleComponentPlacer(); }); + connect(ui->actionAddOR, &QAction::triggered, this, [this]() {componentType = eComponent::OR , resourcePath = ":/components/or.png"; ToggleComponentPlacer(); }); + connect(ui->actionAddNOR, &QAction::triggered, this, [this]() {componentType = eComponent::NOR , resourcePath = ":/components/nor.png"; ToggleComponentPlacer(); }); + connect(ui->actionAddXOR, &QAction::triggered, this, [this]() {componentType = eComponent::XOR, resourcePath = ":/components/xor.png"; ToggleComponentPlacer(); }); + connect(ui->actionAddXNOR, &QAction::triggered, this, [this]() {componentType = eComponent::XNOR , resourcePath = ":/components/xnor.png"; ToggleComponentPlacer(); }); + connect(ui->actionAddSwitch,&QAction::triggered, this, [this]() {componentType = eComponent::SWITCH , resourcePath = ":/components/switch_off.png"; ToggleComponentPlacer(); }); + connect(ui->actionAddLamp, &QAction::triggered, this, [this]() {componentType = eComponent::LAMP , resourcePath = ":/components/lamp_off.png"; ToggleComponentPlacer(); }); + + pen.setColor(Qt::black); + pen.setWidth(3); } Window::~Window() @@ -60,12 +80,13 @@ Window::~Window() void Window::mousePressEvent(QMouseEvent* event) { + mouseDown = true; if (simulating) return; Component* child = componentAt(event->pos()); QAction* toggledAction = componentGroup->checkedAction(); - if (toggledAction == nullptr) + if (toggledAction == ui->actionCursor) { if (child == nullptr) return; @@ -73,49 +94,106 @@ void Window::mousePressEvent(QMouseEvent* event) dragInfo.component = static_cast(child); dragInfo.relativePos = event->pos() - child->pos(); } + else if (toggledAction == ui->actionWiring) + { + if (child == nullptr) + return; + + QPoint mousePos = ui->centralwidget->mapFromParent(event->pos()); + mousePos.setY(mousePos.y() + ui->toolBar->height()); + wires.push_back(QLine(child->CenterPos(), mousePos)); + // std::cout << "(" << child->x() << ", " << child->y() << ") --> (" << mousePos.x() << ", " << mousePos.y() << ")" << std::endl; + + } else { if (child != nullptr) return; - Component* leak = new Component(this, resourcePath); - QPoint pos = event->pos() - QPoint {50, 25}; - leak->setGeometry(QRect(pos.x(), pos.y(), 100, 50)); - leak->setFrameShape(QFrame::NoFrame); - leak->setFrameShadow(QFrame::Plain); - leak->setVisible(true); - leak->setAttribute(Qt::WA_TransparentForMouseEvents); - - components.push_back(leak); + createComponent(event->pos()); } } void Window::mouseMoveEvent(QMouseEvent* event) { + if (simulating) return; + if (dragInfo.component != nullptr) + { dragInfo.component->move(event->pos() - dragInfo.relativePos); + // Move wires too lol + } if (ghostImage != nullptr) ghostImage->move(event->pos() - QPoint{50, ui->toolBar->height() + 25}); // TODO: Hardcoded + if ((componentGroup->checkedAction() == ui->actionWiring) && mouseDown) + { + QPoint mousePos = ui->centralwidget->mapFromParent(event->pos()); + mousePos.setY(mousePos.y() + ui->toolBar->height()); + wires.back().setP2(mousePos); + } + event->accept(); } void Window::mouseReleaseEvent(QMouseEvent* event) { + if (simulating) return; + mouseDown = false; + + if (componentGroup->checkedAction() == ui->actionWiring) + { + Component* connectFrom = componentAt(wires.back().p1()); + Component* connectTo = componentAt(event->pos()); + if (connectTo == nullptr || connectTo == connectFrom) + { + wires.pop_back(); + } + else + { + wires.back().setP2(connectTo->CenterPos()); + connectTo->Connect(connectFrom); + } + } + else if(componentGroup->checkedAction() == ui->actionCursor) + { + Component* clicked = componentAt(event->pos()); + if (clicked != nullptr) + if (clicked->Type() == "switch") + static_cast(clicked)->Toggle(); + } + dragInfo.component = nullptr; } +void Window::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + painter.setPen(pen); + + for(QLine line : wires) + painter.drawLine(line); + + update(); +} + void Window::ToggleSimulation() { simulating = !simulating; ui->actionStart->setIcon(QIcon(simulating ? ":/toolbar/stop.png" : ":/toolbar/start.png")); + + for (Component* c : components) + { + if (c->Type() == "lamp") + static_cast(c)->Turn(c->Action()); + } } void Window::ToggleComponentPlacer() { QAction* toggledAction = componentGroup->checkedAction(); - if (toggledAction == nullptr) + if (toggledAction == ui->actionCursor || toggledAction == ui->actionWiring) { delete ghostImage; ghostImage = nullptr; @@ -163,3 +241,35 @@ Component* Window::componentAt(const QPoint& point) return nullptr; } + +Component* Window::createComponent(const QPoint& position) +{ + Component* leak = nullptr; + + switch (componentType) + { + case eComponent::NOT: leak = new NOTGate(this); break; + case eComponent::AND: leak = new ANDGate(this); break; + case eComponent::NAND: leak = new NANDGate(this); break; + case eComponent::OR: leak = new ORGate(this); break; + case eComponent::NOR: leak = new NORGate(this); break; + case eComponent::XOR: leak = new XORGate(this); break; + case eComponent::XNOR: leak = new XNORGate(this); break; + case eComponent::SWITCH: leak = new Switch(this); break; + case eComponent::LAMP: leak = new Lamp(this); break; + } + + if (leak != nullptr) + { + QPoint pos = position - QPoint{ 50, 25 }; + leak->setGeometry(QRect(pos.x(), pos.y(), 100, 50)); + leak->setFrameShape(QFrame::NoFrame); + leak->setFrameShadow(QFrame::Plain); + leak->setVisible(true); + leak->setAttribute(Qt::WA_TransparentForMouseEvents); + + components.push_back(leak); + } + + return leak; +} diff --git a/ui/Window.ui b/ui/Window.ui index 72a389c..35e0744 100644 --- a/ui/Window.ui +++ b/ui/Window.ui @@ -30,6 +30,9 @@ + + + @@ -37,6 +40,8 @@ + + @@ -183,6 +188,63 @@ 7 + + + true + + + + :/components/switch.png:/components/switch.png + + + AddSwitch + + + + + true + + + + :/components/lamp.png:/components/lamp.png + + + AddLamp + + + + + true + + + true + + + + :/toolbar/cursor.png:/toolbar/cursor.png + + + Cursor + + + Default Mode + + + + + true + + + + :/toolbar/wire.png:/toolbar/wire.png + + + Wiring + + + Wiring mode + + diff --git a/ui/assets/cursor.png b/ui/assets/cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..ec6ed9d84388202921e28098cf030e153cfe05be GIT binary patch literal 5083 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 literal 0 HcmV?d00001 diff --git a/ui/assets/lamp.png b/ui/assets/lamp.png new file mode 100644 index 0000000000000000000000000000000000000000..55ae9fc9ca1c19c0af83257de9cf93d5c6289e84 GIT binary patch literal 711 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 LEcCDIx<~#OXFjee literal 0 HcmV?d00001 diff --git a/ui/assets/lamp_on.png b/ui/assets/lamp_on.png new file mode 100644 index 0000000000000000000000000000000000000000..7fc2c2f36fd7ecb8a2ca3210b3d1c1264f59196b GIT binary patch literal 9101 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 start.png stop.png + cursor.png + wire.png and.png @@ -11,5 +13,11 @@ not.png xnor.png xor.png + lamp.png + lamp_off.png + lamp_on.png + switch.png + switch_off.png + switch_on.png diff --git a/ui/assets/switch.png b/ui/assets/switch.png new file mode 100644 index 0000000000000000000000000000000000000000..ded46aff5d06fb7a18e93eb9082c2fa15ba5fea6 GIT binary patch literal 2190 zcmV;92yyp`P)EX>4Tx04R}tkv&MmKpe$i(@I6E4t5Z6$j~}j5EXHhDi*;)X)CnqU~=gfG-*gu zTpR`0f`cE6RRz0uC=M1fs7x0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbT zcu7P-RCwC$oqcFmRT#%VZsw-bm9}zQX12Liij}moGPR^M&0Z{s2rG~xC5R#lk_rl= zH&LVP1(bx|pdjmys0fwvk6DSN;nFfoC0lB~Ot-ak`}N0jM!#ax_Pfuy=iGDe7asP) zxI5>3e&^hCp65B|K&DKYPON^Om#rSC18RV?jg0`xfI)d*OMt(CKYz6w?qz$(u{{MU{Gj{yBcgE7s_baDx`bwE`p zFdjEEn_R-J8Mq?pFrEznM836}cVHs5G%y(XGqGt&gE2o)7~R$aj7Mz|Ee>6CeCoE}Dpzie`E+Zm0reVz|D?;j0AoJj^z2r@~tnwKce&@ zEm&I=XDRoSDY8CLUR*7}@2q_9^%Qmz(;Bj-Elg9f=hFTJ$%D#zOdnK=1ZHe!cs${F zdYmY|Pkb@#jRwwzS+k#5GjNk&up3wo+zOnUVfgm~({(;p7ZSN#3fxG_cSC_h`h44T zvk_QqdT?3VfD|h)61A-ZSPMK1oR=0Tmti-vL(_f&79l~ZqL861B6-<|yO9v`1t9_% zU{=~mO%2@u%nR+Eb2<|ANpjBaLEw2{cqmZnk@V7}CsVfp&xO)sLV=P);{G><0Od@x z9tdfZ&fug&8H8kagaYL|l!3f8UmC*fJ;M}7C!_W2ms1K>}2UX(GcVuk>-R$ zX(q5ZO$m}U-k4x0TTt9*=#$2oIsdK%LU}o4e^@`{n7_rw378ZpGt%Nk9%tO~#cXYKEa_ z6#>Q(nl+kglzYmTg?3q6L&0z)S~w!_YyS65z;&KMnXfoQYNmk05c*ka>@)cP9lD2+ z{M(#IKuVAtEsgWF+KG0oRIfElv0MANrQjXK`t|wD_(JB&+Y~GBqjz+Z;>$6*P1)ZVwFTNdpz1)e#2}kRzG2bf0HHY89V#G_FF=RebTa zV*k|8zDUcRawyJ1*DJ4TU-bmW7{xkmj=S)hu7>0Ik#wdx<$XKu!Y@(oCEI~h9Kx8c zT%QNyuD~SpJ)~>RYB_F=MlP4stjqYgD^Y}<{HfLG9QV{))uDf>_Y`a_np+DbTe@Fv>>|`;kqg z)hS%$Qe%b#*K3y+ za;3%~vF|D~o>!W6&OjvoQG&!mI?TA=ZEeUAO1q4&t=qO6?{ub2nG&VH0o+(p#w?|5 Q?*IS*07*qoM6N<$g4QPQYXATM literal 0 HcmV?d00001 diff --git a/ui/assets/switch_off.png b/ui/assets/switch_off.png new file mode 100644 index 0000000000000000000000000000000000000000..5406d258424fb5892da04c66d1df12198344b120 GIT binary patch literal 8920 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 D^Ctih literal 0 HcmV?d00001 diff --git a/ui/assets/wire.png b/ui/assets/wire.png new file mode 100644 index 0000000000000000000000000000000000000000..aefa9744473233337a28e417e43ab547fb3216f2 GIT binary patch literal 789 zcmeAS@N?(olHy`uVBq!ia0vp^DImc}*5h!1NUMMFQYF#$GJ(Pq0du1SL<^^c=`^)o3d^{9@X6Bp3riF|3kyYJxcUC+ zgvrLtDJ?A!WjQD>pzf~jDz5o)-;xVT$JnQTFT7{n{`(zsOIYv9wEHRxvZhV+Ja)7u zF_dSI5(lH=suf3kY^TPaIapkK_we!WyCu$l+V}mrR8W^1bB)qRo6Ef270nf8moHhq z7jBPq4eB`GxtPt=zw*Z2#)Vvz41)9~ueyDfr-h*p@D-#fDwruyY$C`D||0ZKeYwv8UAY7 zl6QB{;yxW&heri1FOOWC;{EnosmYY*-!jT)qi91mf&_N`_ItU`%=ZrN@VUG9q}r1| uWz#1xedN8k{?Pv`z8wt+H=?ljePh%17ca{?HU}7T3=E#GelF{r5}E*wH#oQe literal 0 HcmV?d00001