wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
Node.cpp
Go to the documentation of this file.
1// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
5
6#include <QGraphicsPixmapItem>
7#include <QPainter>
8#include <QSvgRenderer>
9
10#include "App/Core/Common.h"
16#include "App/IO/VersionInfo.h"
18#include "App/Wiring/Port.h"
19
20template<>
23 .type = ElementType::Node,
24 .group = ElementGroup::Gate,
25 .minInputSize = 1,
26 .maxInputSize = 1,
27 .minOutputSize = 1,
28 .maxOutputSize = 1,
29 .canChangeAppearance = true,
30 .hasLabel = true,
31 };
32 static_assert(validate(constraints));
33
35 {
37 meta.pixmapPath = []{ return QStringLiteral(":/Components/Logic/node.svg"); };
38 meta.titleText = QT_TRANSLATE_NOOP("Node", "NODE");
39 meta.translatedName = QT_TRANSLATE_NOOP("Node", "Node");
40 meta.trContext = "Node";
41 meta.defaultAppearances = QStringList({":/Components/Logic/node.svg"});
42 return meta;
43 }
44
45 static inline const bool registered = []() {
47 ElementFactory::registerCreator(constraints.type, [] { return new Node(); });
48 return true;
49 }();
50};
51
52// Node is a wire junction / fan-out point: 1 input, 1 output (1:1 pass-through).
53// Its sole logical role is to create a named connection split on the canvas.
54Node::Node(QGraphicsItem *parent)
56{
57 // A node's input is required (cannot be left unconnected) because its sole
58 // purpose is to act as a visible junction/fan-out point on a wire.
59 // In Rx mode setWirelessMode() will relax this to optional.
60 inputPort()->setRequired(true);
61
62 // The node body is 32×32; move the label just below it (instead of the
63 // default 64px offset which is designed for 64×64 elements).
64 setLabelAnchor(QPointF(0, 32));
65
67}
68
70{
71 // Position ports at the left and right midpoints of the 32×32 node icon so
72 // they align with the horizontal wire grid (y=16 is the vertical centre).
73 inputPort()->setPos( 0, 16);
74 outputPort()->setPos(32, 16);
75}
76
78{
80 updateWirelessColor(Status::Unknown);
81 return;
82 }
83 const auto status = simInputs().at(0);
84 setOutputValue(status);
85 updateWirelessColor(status);
86}
87
88// --- Wireless mode ---
89
90static QPixmap renderWirelessPixmap(const QColor &color)
91{
92 const auto hex = color.name();
93 const auto svg = QStringLiteral(
94 "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 16 16\">"
95 "<circle cx=\"1.5\" cy=\"14.5\" r=\"1.5\" fill=\"%1\"/>"
96 "<path d=\"M 1.5,11 A 3.5,3.5 0 0 1 5,14.5\" fill=\"none\" stroke=\"%1\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>"
97 "<path d=\"M 1.5,7 A 7.5,7.5 0 0 1 9,14.5\" fill=\"none\" stroke=\"%1\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>"
98 "<path d=\"M 1.5,3 A 11.5,11.5 0 0 1 13,14.5\" fill=\"none\" stroke=\"%1\" stroke-width=\"1.5\" stroke-linecap=\"round\"/>"
99 "</svg>").arg(hex);
100
101 QSvgRenderer renderer(svg.toUtf8());
102 QPixmap pixmap(20, 20);
103 pixmap.fill(Qt::transparent);
104 QPainter painter(&pixmap);
105 renderer.render(&painter);
106 painter.end();
107 return pixmap;
108}
109
111{
112 return m_wirelessMode;
113}
114
116{
117 return true;
118}
119
121{
122 if (m_wirelessMode == mode) {
123 return;
124 }
125 m_wirelessMode = mode;
126 // Rx nodes receive their signal wirelessly; no physical wire required on the input port.
127 // Tx nodes and plain nodes still need a physical wire driving them.
128 // Guard against a redundant setRequired() call triggering updateConnections()
129 // and causing a visible status flash until the simulation runs. Skip if unchanged.
130 const bool inputRequired = (mode != WirelessMode::Rx);
131 if (inputPort()->isRequired() != inputRequired) {
132 inputPort()->setRequired(inputRequired);
133 }
134
135 // Hide the port that is replaced by the wireless channel.
136 inputPort()->setVisible(mode != WirelessMode::Rx);
137 outputPort()->setVisible(mode != WirelessMode::Tx);
138
139 if (mode == WirelessMode::None) {
140 if (m_wirelessIndicator) {
141 m_wirelessIndicator->setVisible(false);
142 }
143 m_wirelessColor = QColor();
144 return;
145 }
146
147 // Create the indicator on first use; render with the Unknown-state color
148 // so it matches undriven wires. updateLogic() will repaint it with the
149 // live signal color once the simulation runs.
150 if (!m_wirelessIndicator) {
152 m_wirelessIndicator = new QGraphicsPixmapItem(renderWirelessPixmap(color), this);
153 m_wirelessIndicator->setZValue(1);
154 m_wirelessColor = color;
155 }
156
157 m_wirelessIndicator->setVisible(true);
158
159 if (mode == WirelessMode::Tx) {
160 // Above the output port (x=32, y=16). The dot in the 20×20 pixmap is at
161 // local (≈2, ≈18); setPos(30, -6) places the dot at node (32, 12) — just
162 // above the port — with arcs fanning upper-right.
163 m_wirelessIndicator->setPos(30, -6);
164 m_wirelessIndicator->setTransform(QTransform());
165 } else {
166 // Above the input port (x=0, y=16), mirrored so arcs open upper-left.
167 // With scale(-1,1): parent_x = setPos.x - local_x, so setPos.x=2 puts the
168 // dot at node (0, 12) — just above the port.
169 m_wirelessIndicator->setPos(2, -6);
170 m_wirelessIndicator->setTransform(QTransform().scale(-1, 1));
171 }
172}
173
174// --- Serialization ---
175
176void Node::save(QDataStream &stream, SerializationOptions options) const
177{
178 GraphicElement::save(stream, options);
179
180 QMap<QString, QVariant> map;
181 // PortableFile streams omit the default; the loader's
182 // map.value("wirelessMode", 0) already resolves a missing key to None.
183 // Snapshots write unconditionally (reloaded into live elements).
184 if (options.purpose != SerializationPurpose::PortableFile || m_wirelessMode != WirelessMode::None) {
185 map.insert("wirelessMode", static_cast<int>(m_wirelessMode));
186 }
187 stream << map;
188}
189
190void Node::load(QDataStream &stream, SerializationContext &context)
191{
192 GraphicElement::load(stream, context);
193
195 return;
196 }
197
198 QMap<QString, QVariant> map;
200
201 // Clamp out-of-range values (e.g. corrupted file) to None.
202 const int raw = map.value("wirelessMode", 0).toInt();
203 setWirelessMode((raw >= 0 && raw <= 2) ? static_cast<WirelessMode>(raw) : WirelessMode::None);
204}
205
206void Node::updateWirelessColor(Status status)
207{
208 if (m_wirelessMode == WirelessMode::None || !m_wirelessIndicator) {
209 return;
210 }
211
212 const auto theme = ThemeManager::attributes();
213 QColor color;
214 switch (status) {
215 case Status::Unknown: color = theme.m_connectionUnknown; break;
216 case Status::Inactive: color = theme.m_connectionInactive; break;
217 case Status::Active: color = theme.m_connectionActive; break;
218 case Status::Error: color = theme.m_connectionError; break;
219 }
220
221 if (color == m_wirelessColor) {
222 return;
223 }
224 m_wirelessColor = color;
225 m_wirelessIndicator->setPixmap(renderWirelessPixmap(color));
226}
Common logging utilities, the Pandaception error type, and helper macros.
Connection: a wire that connects an output port to an input port in the circuit scene.
Singleton factory for all circuit element types.
Self-registering element trait template and compile-time constraint validation.
ElementMetadata metadataFromConstraints(const ElementConstraints &c)
Converts ElementConstraints to an ElementMetadata with all constraint-derived fields set.
Definition ElementInfo.h:80
constexpr bool validate(const ElementConstraints &c)
Validates element constraints at compile time.
Definition ElementInfo.h:48
Enums::ElementType ElementType
Definition Enums.h:107
Enums::Status Status
Definition Enums.h:106
Enums::WirelessMode WirelessMode
Definition Enums.h:109
static QPixmap renderWirelessPixmap(const QColor &color)
Definition Node.cpp:90
Graphic element for a wire junction node.
Port classes: Port (base), InputPort, and OutputPort.
Deserialization/serialization context structs passed through load()/save() call chains.
Circuit and waveform file serialization/deserialization utilities.
Theme management types and singleton ThemeManager.
Named version predicates for file-format compatibility checks.
static void registerCreator(ElementType type, std::function< GraphicElement *()> creator)
Registers a creator lambda for type, used by buildElement().
static void registerMetadata(const ElementMetadata &meta)
Registers meta in the global map (called once per element type at startup).
GraphicElement(ElementType type, QGraphicsItem *parent=nullptr)
Constructs a graphic element of the given type, fetching all properties from the metadata registry.
const QVector< Status > & simInputs() const
Read-only view of the cached simulation input values.
void setLabelAnchor(const QPointF &pos)
bool simUpdateInputsAllowUnknown()
Like simUpdateInputs(), but allows Unknown/Error values through.
void setOutputValue(const int index, const Status value)
Sets simulation output port index to value.
virtual void load(QDataStream &stream, SerializationContext &context)
Loads the graphic element through a binary data stream.
InputPort * inputPort(const int index=0) const
Returns the input port at index (default 0).
virtual QString color() const
Returns the name of the color currently applied to this element.
OutputPort * outputPort(const int index=0) const
Returns the output port at index (default 0).
virtual void save(QDataStream &stream, SerializationOptions options) const
Visible wire junction (T-junction) element with 1 input and 1 output.
Definition Node.h:24
Node(QGraphicsItem *parent=nullptr)
Constructs the element with optional parent.
Definition Node.cpp:54
WirelessMode wirelessMode() const override
Returns the current wireless routing mode (None / Tx / Rx).
Definition Node.cpp:110
void setWirelessMode(WirelessMode mode)
Sets the wireless routing mode and adjusts port requirements accordingly.
Definition Node.cpp:120
void updatePortsProperties() override
Recalculates port positions for the current port count.
Definition Node.cpp:69
bool hasWirelessMode() const override
Returns true — Node supports wireless mode configuration.
Definition Node.cpp:115
void save(QDataStream &stream, SerializationOptions options) const override
Definition Node.cpp:176
void load(QDataStream &stream, SerializationContext &context) override
Loads the graphic element through a binary data stream.
Definition Node.cpp:190
void updateLogic() override
Copies the single input value to the single output (pass-through).
Definition Node.cpp:77
void setRequired(const bool required)
Marks whether a wire to this port is mandatory.
Definition Port.cpp:199
static QMap< QString, QVariant > readBoundedMetadata(QDataStream &stream)
Reads the file-level metadata QMap<QString,QVariant> from stream without calling QList::reserve() wit...
QColor m_connectionUnknown
static const ThemeAttributes & attributes()
Returns the current ThemeAttributes color set.
bool hasWirelessMode(const QVersionNumber &v)
V4.4: Node wireless mode stored in file.
Definition VersionInfo.h:69
Compile-time-validatable subset of ElementMetadata.
Definition ElementInfo.h:21
static const bool registered
Definition Node.cpp:45
static ElementMetadata metadata()
Definition Node.cpp:34
static constexpr ElementConstraints constraints
Definition Node.cpp:22
Self-registering element information trait.
Compile-time-registered properties for one element type.
Bundles all per-deserialization state so that load() overrides receive it through one explicit parame...
QVersionNumber version
File-format version read from the stream header.
Options passed to GraphicElement::save() (and friends); the save-side counterpart of SerializationCon...
SerializationPurpose purpose
What this serialization is for; see SerializationPurpose. Mandatory, no default.