wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
Led.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 <bitset>
7
12#include "App/IO/VersionInfo.h"
13#include "App/Wiring/Port.h"
14
15template<>
18 .type = ElementType::Led,
19 .group = ElementGroup::Output,
20 .minInputSize = 1,
21 .maxInputSize = 4,
22 .canChangeAppearance = true,
23 .hasColors = true,
24 .hasLabel = true,
25 .rotatesGraphic = false,
26 };
27 static_assert(validate(constraints));
28
30 {
32 meta.pixmapPath = []{ return QStringLiteral(":/Components/Output/Led/LedOff.svg"); };
33 meta.titleText = QT_TRANSLATE_NOOP("Led", "LED");
34 meta.translatedName = QT_TRANSLATE_NOOP("Led", "LED");
35 meta.trContext = "Led";
36 meta.defaultAppearances = QStringList({
37 // Single input values:
38 ":/Components/Output/Led/LedOff.svg", // 0
39 ":/Components/Output/Led/WhiteLed.svg", // 1
40 ":/Components/Output/Led/LedOff.svg", // 2
41 ":/Components/Output/Led/RedLed.svg", // 3
42 ":/Components/Output/Led/LedOff.svg", // 4
43 ":/Components/Output/Led/GreenLed.svg", // 5
44 ":/Components/Output/Led/LedOff.svg", // 6
45 ":/Components/Output/Led/BlueLed.svg", // 7
46 ":/Components/Output/Led/LedOff.svg", // 8
47 ":/Components/Output/Led/PurpleLed.svg", // 9
48 // Multiple input values:
49 ":/Components/Output/Led//BlackLed.png", // 10
50 ":/Components/Output/Led//NavyBlueLed.png", // 11
51 ":/Components/Output/Led//GreenLed.png", // 12
52 ":/Components/Output/Led//TealLed.png", // 13
53 ":/Components/Output/Led//DarkRedLed.png", // 14
54 ":/Components/Output/Led//MagentaLed.png", // 15
55 ":/Components/Output/Led//OrangeLed.png", // 16
56 ":/Components/Output/Led//LightGrayLed.png", // 17
57
58 ":/Components/Output/Led/LedOff.svg", // 18
59 ":/Components/Output/Led/RoyalLed.png", // 19
60 ":/Components/Output/Led/LimeGreenLed.png", // 20
61 ":/Components/Output/Led/AquaLightLed.png", // 21
62 ":/Components/Output/Led/RedLed.png", // 22
63 ":/Components/Output/Led/HotPinkLed.png", // 23
64 ":/Components/Output/Led/YellowLed.png", // 24
65 ":/Components/Output/Led/WhiteLed.png", // 25
66 });
67 return meta;
68 } // LCOV_EXCL_LINE — recurring pattern 1: compiler-generated cleanup for the returned ElementMetadata's QString/QStringList members, never reached after the return above.
69
70 static inline const bool registered = []() {
72 ElementFactory::registerCreator(constraints.type, [] { return new Led(); });
73 return true;
74 }();
75};
76
77Led::Led(QGraphicsItem *parent)
79{
80 // The appearance list is indexed by colorIndex(). For a 1-input LED the index is
81 // m_colorIndex + input_value, where m_colorIndex is set by setColor() to the
82 // base offset for the chosen color (0=White, 2=Red, 4=Green, 6=Blue, 8=Purple).
83 // Even indices are the Off state; odd indices are the On state.
84 // Indices 10-25 cover multi-input (2/3/4-bit) color palettes (see comment below).
85 m_appearance.seedFromMetadata(ElementMetadataRegistry::metadata(ElementType::Led).defaultAppearances, {}, {});
86 setPixmap(0);
87
88 setHasColors(true);
89
91}
92
93/* Color pallets:
94 *
95 * 4 bit 3 bit 2 bit
96 * 0000: black, 000: dark gray, 00: dark gray,
97 * 0001: blue, 001: light blue, 01: light blue,
98 * 0010: green, 010: light green, 10: light green,
99 * 0011: teal, 011: yellow, 11: light red
100 * 0100: red, 100: light red,
101 * 0101: magenta, 101: magenta,
102 * 0110: orange, 110: cyan,
103 * 0111: light gray 111: white
104 * 1000: dark gray,
105 * 1001: light blue,
106 * 1010: light green,
107 * 1011: cyan,
108 * 1100: light red,
109 * 1101: pink,
110 * 1110: yellow,
111 * 1111: white
112 */
113
114int Led::colorIndex()
115{
116 if (!isValid()) {
117 return 0;
118 }
119
120 // Encode the state of all input ports as a binary number.
121 // Bit 0 = port 0, bit 1 = port 1, etc. (LSB-first ordering).
122 std::bitset<4> indexBit;
123
124 for (int i = 0; i < inputSize(); ++i) {
125 indexBit[static_cast<std::size_t>(i)] = (inputPort(i)->status() == Status::Active);
126 }
127
128 const int index = static_cast<int>(indexBit.to_ulong());
129
130 int index2 = 0;
131
132 // Map the encoded value to an appearance list index depending on how many inputs
133 // the LED has. Multi-input LEDs use a fixed color palette (indices 10-25);
134 // single-input LEDs offset by m_colorIndex to pick the user-selected color.
135 // For 2-input, index 3 (both on) maps to index 25 (white "on") rather than
136 // index 21 (aqua) to preserve backward-compatible color behavior.
137 switch (inputSize()) {
138 case 1: index2 = m_colorIndex + index; break;
139 case 2: index2 = (index == 3) ? 25 : 18 + index; break;
140 case 3: index2 = 18 + index; break;
141 case 4: index2 = 10 + index; break;
142
143 default: // LCOV_EXCL_LINE
144 // Unreachable: GraphicElement::setPortSize() rejects any inputSize() request
145 // outside [minInputSize(), maxInputSize()] = [1, 4] as a no-op, so inputSize()
146 // can never legitimately be anything this switch doesn't already handle.
147 index2 = index; // LCOV_EXCL_LINE
148 break; // LCOV_EXCL_LINE
149 }
150
151 return index2;
152}
153
155{
156 setPixmap(colorIndex());
157}
158
159void Led::setColor(const QString &color)
160{
161 m_color = color;
162
163 // m_colorIndex is the base offset into the single-input appearance list.
164 // Each color occupies two consecutive entries: [base]=Off, [base+1]=On.
165 m_colorIndex = 2 * colorNameToIndex(color);
166}
167
168QString Led::color() const
169{
170 return m_color;
171}
172
173void Led::save(QDataStream &stream, SerializationOptions options) const
174{
175 GraphicElement::save(stream, options);
176
177 QMap<QString, QVariant> map;
178 // PortableFile streams omit the default; fresh-loaded elements start there
179 // anyway. Snapshots write unconditionally (reloaded into live elements).
181 map.insert("color", color());
182 }
183
184 stream << map;
185}
186
187void Led::load(QDataStream &stream, SerializationContext &context)
188{
189 GraphicElement::load(stream, context);
190
191 if ((VersionInfo::hasClock(context.version)) && (!VersionInfo::hasQMapFormat(context.version))) {
192 // v1.1–4.0 stored color as a bare QString
193 const QString color_ = Serialization::readBoundedString(stream);
194 setColor(color_);
195 }
196
197 if (VersionInfo::hasQMapFormat(context.version)) {
198 // v4.1+ uses a key-value map
199 QMap<QString, QVariant> map = Serialization::readBoundedMetadata(stream);
200
201 if (map.contains("color")) {
202 setColor(map.value("color").toString());
203 }
204 }
205}
206
208{
209 return color();
210}
211
213{
214 // Color selection is only meaningful for single-input LEDs; with multiple
215 // inputs the color is determined entirely by the input bit pattern.
216 setHasColors(inputSize() == 1);
217
218 for (auto *port : inputs()) {
219 port->setName(QString::number(inputs().indexOf(port) + 1));
220 port->setRequired(false);
221 port->setDefaultStatus(Status::Inactive);
222 }
223
225}
226
227void Led::setAppearance(const bool useDefaultAppearance, const QString &fileName)
228{
229 const int index = colorIndex();
230
231 if (useDefaultAppearance) {
232 m_appearance.resetAlternativeToDefault();
233 } else {
234 // Replace only the appearance for the currently active color/state, so other
235 // color states continue to use their default images.
236 m_appearance.setAlternativeAppearanceAt(index, fileName);
237 }
238
239 m_appearance.setUsingDefaultAppearance(useDefaultAppearance);
240 setPixmap(index);
241}
242
243QList<std::pair<int, QString>> Led::appearanceStates() const
244{
245 QList<std::pair<int, QString>> states;
246
247 // Builds "Port 1=0, Port 2=1, ..." from the bits of `code` (bit 0 = port 1, matching
248 // colorIndex()'s bit ordering and the "1"/"2"/... port names set in updatePortsProperties()).
249 auto portStateLabel = [](const int numPorts, const int code) {
250 QStringList parts;
251 for (int port = 0; port < numPorts; ++port) {
252 parts << tr("Port %1=%2").arg(port + 1).arg((code >> port) & 1);
253 }
254 return parts.join(QStringLiteral(", "));
255 };
256
257 switch (inputSize()) {
258 case 1:
259 states.append({m_colorIndex, tr("Off")});
260 states.append({m_colorIndex + 1, tr("On")});
261 break;
262
263 case 2:
264 for (int i = 0; i < 4; ++i) {
265 const int listIndex = (i == 3) ? 25 : 18 + i; // preserves colorIndex()'s special-case mapping
266 states.append({listIndex, portStateLabel(2, i)});
267 }
268 break;
269
270 case 3:
271 for (int i = 0; i < 8; ++i) {
272 states.append({18 + i, portStateLabel(3, i)});
273 }
274 break;
275
276 case 4:
277 for (int i = 0; i < 16; ++i) {
278 states.append({10 + i, portStateLabel(4, i)});
279 }
280 break;
281
282 default: // LCOV_EXCL_LINE
283 // Unreachable for the same reason as colorIndex()'s default case above:
284 // inputSize() can never be outside [1, 4].
285 states.append({0, tr("Default")}); // LCOV_EXCL_LINE
286 break; // LCOV_EXCL_LINE
287 }
288
289 return states;
290} // LCOV_EXCL_LINE — recurring pattern 1: compiler-generated cleanup for the returned QList<std::pair<int, QString>>, never reached after the return above.
291
293{
294 if (!simUpdateInputs()) {
295 return;
296 }
297 for (int i = 0; i < simInputs().size(); ++i) {
298 setOutputValue(i, simInputs().at(i));
299 }
300}
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
Graphic element for the LED output indicator.
Port classes: Port (base), InputPort, and OutputPort.
Deserialization/serialization context structs passed through load()/save() call chains.
Circuit and waveform file serialization/deserialization utilities.
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).
static const ElementMetadata & metadata(ElementType type)
Returns the metadata for type. Asserts if type is not registered.
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 setHasColors(const bool hasColors)
Sets whether this element type supports color selection.
ElementAppearance m_appearance
int inputSize() const
Returns the current number of input ports.
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).
static int colorNameToIndex(const QString &color)
Maps a color name ("White","Red","Green","Blue","Purple") to indices 0–4.
bool simUpdateInputs()
Snapshots each predecessor's output into the simulation input cache.
virtual void save(QDataStream &stream, SerializationOptions options) const
void setPixmap(const QString &pixmapPath)
Loads and applies the pixmap located at pixmapPath.
bool isValid()
Returns true if the element is fully initialised and connected correctly.
virtual void updatePortsProperties()
Repositions and reconfigures all ports after the port count changes.
const QVector< InputPort * > & inputs() const
Returns a const reference to the vector of all input ports.
LED output element that lights up when its input is logic-1.
Definition Led.h:20
Led(QGraphicsItem *parent=nullptr)
Constructs the element with optional parent.
Definition Led.cpp:77
QString color() const override
Returns the current LED color name.
Definition Led.cpp:168
void updateLogic() override
Propagates input values to matching outputs.
Definition Led.cpp:292
QList< std::pair< int, QString > > appearanceStates() const override
Definition Led.cpp:243
void setColor(const QString &color) override
Sets the LED color to color.
Definition Led.cpp:159
void load(QDataStream &stream, SerializationContext &context) override
Definition Led.cpp:187
void updatePortsProperties() override
Updates port positions when input count changes.
Definition Led.cpp:212
void save(QDataStream &stream, SerializationOptions options) const override
Definition Led.cpp:173
QString genericProperties() override
Returns a string describing the current color for the element editor.
Definition Led.cpp:207
void setAppearance(const bool useDefaultAppearance, const QString &fileName) override
Definition Led.cpp:227
void refresh() override
Refreshes the visual appearance based on current state.
Definition Led.cpp:154
static constexpr const char * kDefaultColor
Definition Led.h:28
static QString readBoundedString(QDataStream &stream)
Reads a QString from stream, refusing to allocate more bytes than remain in the stream (prevents OOM ...
static QMap< QString, QVariant > readBoundedMetadata(QDataStream &stream)
Reads the file-level metadata QMap<QString,QVariant> from stream without calling QList::reserve() wit...
bool hasClock(const QVersionNumber &v)
V1.1: Clock element added.
Definition VersionInfo.h:21
bool hasQMapFormat(const QVersionNumber &v)
V4.1: Format changed from flat binary to keyed QMap; port serial IDs; rotation fix.
Definition VersionInfo.h:60
Compile-time-validatable subset of ElementMetadata.
Definition ElementInfo.h:21
static const bool registered
Definition Led.cpp:70
static ElementMetadata metadata()
Definition Led.cpp:29
static constexpr ElementConstraints constraints
Definition Led.cpp:17
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.