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 }
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:
144 index2 = index;
145 break;
146 }
147
148 return index2;
149}
150
152{
153 setPixmap(colorIndex());
154}
155
156void Led::setColor(const QString &color)
157{
158 m_color = color;
159
160 // m_colorIndex is the base offset into the single-input appearance list.
161 // Each color occupies two consecutive entries: [base]=Off, [base+1]=On.
162 m_colorIndex = 2 * colorNameToIndex(color);
163}
164
165QString Led::color() const
166{
167 return m_color;
168}
169
170void Led::save(QDataStream &stream, SerializationOptions options) const
171{
172 GraphicElement::save(stream, options);
173
174 QMap<QString, QVariant> map;
175 // PortableFile streams omit the default; fresh-loaded elements start there
176 // anyway. Snapshots write unconditionally (reloaded into live elements).
178 map.insert("color", color());
179 }
180
181 stream << map;
182}
183
184void Led::load(QDataStream &stream, SerializationContext &context)
185{
186 GraphicElement::load(stream, context);
187
188 if ((VersionInfo::hasClock(context.version)) && (!VersionInfo::hasQMapFormat(context.version))) {
189 // v1.1–4.0 stored color as a bare QString
190 const QString color_ = Serialization::readBoundedString(stream);
191 setColor(color_);
192 }
193
194 if (VersionInfo::hasQMapFormat(context.version)) {
195 // v4.1+ uses a key-value map
196 QMap<QString, QVariant> map = Serialization::readBoundedMetadata(stream);
197
198 if (map.contains("color")) {
199 setColor(map.value("color").toString());
200 }
201 }
202}
203
205{
206 return color();
207}
208
210{
211 // Color selection is only meaningful for single-input LEDs; with multiple
212 // inputs the color is determined entirely by the input bit pattern.
213 setHasColors(inputSize() == 1);
214
215 for (auto *port : inputs()) {
216 port->setName(QString::number(inputs().indexOf(port) + 1));
217 port->setRequired(false);
218 port->setDefaultStatus(Status::Inactive);
219 }
220
222}
223
224void Led::setAppearance(const bool useDefaultAppearance, const QString &fileName)
225{
226 const int index = colorIndex();
227
228 if (useDefaultAppearance) {
229 m_appearance.resetAlternativeToDefault();
230 } else {
231 // Replace only the appearance for the currently active color/state, so other
232 // color states continue to use their default images.
233 m_appearance.setAlternativeAppearanceAt(index, fileName);
234 }
235
236 m_appearance.setUsingDefaultAppearance(useDefaultAppearance);
237 setPixmap(index);
238}
239
240QList<std::pair<int, QString>> Led::appearanceStates() const
241{
242 QList<std::pair<int, QString>> states;
243
244 // Builds "Port 1=0, Port 2=1, ..." from the bits of `code` (bit 0 = port 1, matching
245 // colorIndex()'s bit ordering and the "1"/"2"/... port names set in updatePortsProperties()).
246 auto portStateLabel = [](const int numPorts, const int code) {
247 QStringList parts;
248 for (int port = 0; port < numPorts; ++port) {
249 parts << tr("Port %1=%2").arg(port + 1).arg((code >> port) & 1);
250 }
251 return parts.join(QStringLiteral(", "));
252 };
253
254 switch (inputSize()) {
255 case 1:
256 states.append({m_colorIndex, tr("Off")});
257 states.append({m_colorIndex + 1, tr("On")});
258 break;
259
260 case 2:
261 for (int i = 0; i < 4; ++i) {
262 const int listIndex = (i == 3) ? 25 : 18 + i; // preserves colorIndex()'s special-case mapping
263 states.append({listIndex, portStateLabel(2, i)});
264 }
265 break;
266
267 case 3:
268 for (int i = 0; i < 8; ++i) {
269 states.append({18 + i, portStateLabel(3, i)});
270 }
271 break;
272
273 case 4:
274 for (int i = 0; i < 16; ++i) {
275 states.append({10 + i, portStateLabel(4, i)});
276 }
277 break;
278
279 default:
280 states.append({0, tr("Default")});
281 break;
282 }
283
284 return states;
285}
286
288{
289 if (!simUpdateInputs()) {
290 return;
291 }
292 for (int i = 0; i < simInputs().size(); ++i) {
293 setOutputValue(i, simInputs().at(i));
294 }
295}
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:165
void updateLogic() override
Propagates input values to matching outputs.
Definition Led.cpp:287
QList< std::pair< int, QString > > appearanceStates() const override
Definition Led.cpp:240
void setColor(const QString &color) override
Sets the LED color to color.
Definition Led.cpp:156
void load(QDataStream &stream, SerializationContext &context) override
Definition Led.cpp:184
void updatePortsProperties() override
Updates port positions when input count changes.
Definition Led.cpp:209
void save(QDataStream &stream, SerializationOptions options) const override
Definition Led.cpp:170
QString genericProperties() override
Returns a string describing the current color for the element editor.
Definition Led.cpp:204
void setAppearance(const bool useDefaultAppearance, const QString &fileName) override
Definition Led.cpp:224
void refresh() override
Refreshes the visual appearance based on current state.
Definition Led.cpp:151
static constexpr const char * kDefaultColor
Definition Led.h:26
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.