wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
Demux.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 <cmath>
7
8#include <QPainter>
9
10#include "App/Core/Constants.h"
13#include "App/Wiring/Port.h"
14
15template<>
18 .type = ElementType::Demux,
19 .group = ElementGroup::Mux,
20 .minInputSize = 2,
21 .maxInputSize = 4,
22 .minOutputSize = 2,
23 .maxOutputSize = 8,
24 .canChangeAppearance = true,
25 };
26 static_assert(validate(constraints));
27
29 {
31 meta.pixmapPath = []{ return QStringLiteral(":/Components/Logic/demux.svg"); };
32 meta.titleText = QT_TRANSLATE_NOOP("Demux", "DEMULTIPLEXER");
33 meta.translatedName = QT_TRANSLATE_NOOP("Demux", "Demux");
34 meta.trContext = "Demux";
35 meta.defaultAppearances = QStringList({":/Components/Logic/demux.svg"});
36 return meta;
37 }
38
39 static inline const bool registered = []() {
41 ElementFactory::registerCreator(constraints.type, [] { return new Demux(); });
42 return true;
43 }();
44};
45
46Demux::Demux(QGraphicsItem *parent)
48{
50}
51
52void Demux::setInputSize(const int size)
53{
54 // Input size is derived from output size and cannot be set directly
55 // This is a private override to prevent accidental calls
56 Q_UNUSED(size)
57 // Do nothing - this method should not be called
58}
59
60void Demux::setOutputSize(const int size)
61{
62 // Validate size is within bounds
63 if ((size < minOutputSize()) || (size > maxOutputSize())) {
64 return; // Silently ignore invalid sizes, stays at current size
65 }
66
67 // Calculate required input ports: 1 data + log2(size) select lines
68 const int numSelectLines = calculateSelectLines(size);
69 int requiredInputs = 1 + numSelectLines;
70
71 // Update output size FIRST to ensure outputSize() reflects new value
72 // This prevents updatePortsProperties() from using stale output count
74
75 // Then, update input size to match the select lines needed
76 // This will trigger updatePortsProperties() with both input and output sizes correct
77 GraphicElement::setInputSize(requiredInputs);
78
79}
80
82{
83 // Determine number of select lines based on number of outputs
84 const int numSelectLines = calculateSelectLines(outputSize());
85
86 const int step = Constants::gridSize / 2; // 8
87
88 // Calculate element height to fit all output ports with padding
89 int outputPortsSpan = (outputSize() - 1) * step * 2;
90 int elementHeight = (std::max)(64, outputPortsSpan + step * 6);
91 // Snap to multiple of 16
92 elementHeight = ((elementHeight + 15) / 16) * 16;
93 int centerY = elementHeight / 2;
94
95 // Position data input on the left, centered
96 inputPort(0)->setPos(16, centerY);
97 inputPort(0)->setName("In");
98
99 // Position select lines at the bottom, spaced by gridSize to avoid overlap
100 int selectY = elementHeight - step;
101 for (int i = 0; i < numSelectLines; ++i) {
102 if ((1 + i) < inputSize()) {
103 inputPort(1 + i)->setPos(32 + (i * step * 2), selectY);
104 inputPort(1 + i)->setName(QString("S%1").arg(i));
105 }
106 }
107
108 // Position output ports on the right, centered vertically
109 int y = centerY - (outputSize() - 1) * step;
110 for (int i = 0; i < outputSize(); ++i) {
111 if (outputPort(i) != nullptr) {
112 outputPort(i)->setPos(48, y);
113 outputPort(i)->setName(QString("Out%1").arg(i));
114 }
115 y += step * 2;
116 }
117
118 generatePixmap();
119}
120
122{
123 return renderBodyBounds();
124}
125
126void Demux::generatePixmap()
127{
128 // The body is drawn as vectors in drawBody()/paint(); m_pixmap is kept only so the base
129 // pixmapCenter()/boundingRect() have the right size (its image content is never displayed).
130 const int height = static_cast<int>(renderBodyBounds().height());
131
132 QPixmap sizingPixmap(64, height);
133 sizingPixmap.fill(Qt::transparent);
134 m_appearance.setRenderPixmap(sizingPixmap);
135 update();
136}
137
138void Demux::drawBody(QPainter *painter)
139{
140 painter->save();
141 painter->setRenderHint(QPainter::Antialiasing);
142 // boundingRect()'s top-left may be negative when ports extend past the 64×64 body; align the
143 // local origin with it so the body lands exactly where the old rasterised pixmap was blitted.
144 painter->translate(boundingRect().topLeft());
145
146 const int height = pixmap().rect().height();
147
148 // Trapezoid body: narrower on left (input side), wider on right (output side)
149 const int bodyTop = 8;
150 const int bodyBottom = height - 10;
151 const int leftInset = 8; // fixed inset for the narrower left side
152
153 // Outer dark shape
154 painter->setBrush(QColor(38, 38, 38));
155 painter->setPen(QPen(QColor(38, 38, 38), 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
156
157 QPolygonF outer;
158 outer << QPointF(22, bodyTop + leftInset)
159 << QPointF(22, bodyBottom - leftInset)
160 << QPointF(42, bodyBottom)
161 << QPointF(42, bodyTop);
162 painter->drawPolygon(outer);
163
164 // Inner white fill
165 painter->setBrush(QColor(252, 252, 252));
166 painter->setPen(QPen(QColor(252, 252, 252), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
167
168 const int fillInset = 4;
169 QPolygonF inner;
170 inner << QPointF(22 + fillInset, bodyTop + leftInset + fillInset - 2)
171 << QPointF(22 + fillInset, bodyBottom - leftInset - fillInset + 2)
172 << QPointF(42 - fillInset, bodyBottom - fillInset)
173 << QPointF(42 - fillInset, bodyTop + fillInset);
174 painter->drawPolygon(inner);
175
176 painter->restore();
177}
178
179void Demux::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
180{
181 Q_UNUSED(widget)
182 Q_UNUSED(option)
183
184 if (isSelected()) {
185 painter->save();
186 painter->setBrush(m_appearance.selectionBrush());
187 painter->setPen(QPen(m_appearance.selectionPen(), 0.5, Qt::SolidLine));
188 painter->drawRoundedRect(boundingRect(), 5, 5);
189 painter->restore();
190 }
191
192 // Draw the body as vectors (crisp at any zoom) rather than blitting a fixed-resolution pixmap.
193 drawBody(painter);
194}
195
197{
199 return;
200 }
201
202 const Status data = simInputs().at(0);
203
204 // Calculate select lines from current output count
205 const int numSelectLines = calculateSelectLines(outputSize());
206
207 // If any select line is Unknown/Error, all outputs are indeterminate
208 for (int i = 0; i < numSelectLines; i++) {
209 const Status sel = simInputs().at(1 + i);
210 if (sel == Status::Unknown || sel == Status::Error) {
211 for (int j = 0; j < outputSize(); j++) {
212 setOutputValue(j, sel);
213 }
214 return;
215 }
216 }
217
218 const int selectValue = decodeSelectValue(1, numSelectLines);
219
220 // Mirror Mux: a select value that addresses no output (possible whenever
221 // the output count is not a power of two) is indeterminate routing — all
222 // outputs become Unknown instead of silently dropping the data.
223 if (selectValue >= outputSize()) {
224 for (int i = 0; i < outputSize(); i++) {
225 setOutputValue(i, Status::Unknown);
226 }
227 return;
228 }
229
230 for (int i = 0; i < outputSize(); i++) {
231 setOutputValue(i, (i == selectValue) ? data : Status::Inactive);
232 }
233}
234
235int Demux::calculateSelectLines(int dataCount)
236{
237 int k = 1;
238 while ((1 << k) < dataCount) {
239 k++;
240 }
241 return k;
242}
Shared numeric constants used across layers.
Graphic element for the Demultiplexer (DEMUX).
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
Port classes: Port (base), InputPort, and OutputPort.
Graphical representation of a demultiplexer (1-to-2^N).
Definition Demux.h:20
void updateLogic() override
Routes the data input to the output selected by the select lines.
Definition Demux.cpp:196
QRectF boundingRect() const override
Definition Demux.cpp:121
void updatePortsProperties() override
Recalculates port positions for the current port count.
Definition Demux.cpp:81
void setOutputSize(const int size) override
Definition Demux.cpp:60
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
Definition Demux.cpp:179
Demux(QGraphicsItem *parent=nullptr)
Constructs the element with optional parent.
Definition Demux.cpp:46
void setRenderPixmap(const QPixmap &pixmap)
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.
int decodeSelectValue(int offset, int count) const
Decodes count select-line statuses from simInputs() into a binary index.
ElementAppearance m_appearance
int inputSize() const
Returns the current number of input ports.
QPixmap pixmap() const
Returns the pixmap currently displayed by this element.
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.
QRectF renderBodyBounds() const
Footprint of a "procedural render body" (IC/Mux/Demux/TruthTable): the nominal 64x64 body unioned wit...
int maxOutputSize() const
Returns the maximum allowed number of output ports.
InputPort * inputPort(const int index=0) const
Returns the input port at index (default 0).
virtual void setInputSize(const int size)
Adjusts the number of input ports to size, adding or removing ports as needed.
int outputSize() const
Returns the current number of output ports.
OutputPort * outputPort(const int index=0) const
Returns the output port at index (default 0).
int minOutputSize() const
Returns the minimum allowed number of output ports.
virtual void setOutputSize(const int size)
Adjusts the number of output ports to size, adding or removing ports as needed.
void setName(const QString &name)
Sets the label text shown next to the port.
Definition Port.cpp:166
constexpr int gridSize
Scene grid unit in pixels (elements snap to gridSize/2).
Definition Constants.h:12
Compile-time-validatable subset of ElementMetadata.
Definition ElementInfo.h:21
static constexpr ElementConstraints constraints
Definition Demux.cpp:17
static ElementMetadata metadata()
Definition Demux.cpp:28
static const bool registered
Definition Demux.cpp:39
Self-registering element information trait.
Compile-time-registered properties for one element type.