wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
Mux.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::Mux,
19 .group = ElementGroup::Mux,
20 .minInputSize = 3,
21 .maxInputSize = 11,
22 .minOutputSize = 1,
23 .maxOutputSize = 1,
24 .canChangeAppearance = true,
25 };
26 static_assert(validate(constraints));
27
29 {
31 meta.pixmapPath = []{ return QStringLiteral(":/Components/Logic/mux.svg"); };
32 meta.titleText = QT_TRANSLATE_NOOP("Mux", "MULTIPLEXER");
33 meta.translatedName = QT_TRANSLATE_NOOP("Mux", "Mux");
34 meta.trContext = "Mux";
35 meta.defaultAppearances = QStringList({":/Components/Logic/mux.svg"});
36 return meta;
37 }
38
39 static inline const bool registered = []() {
41 ElementFactory::registerCreator(constraints.type, [] { return new Mux(); });
42 return true;
43 }();
44};
45
46Mux::Mux(QGraphicsItem *parent)
48{
50}
51
53{
54 // Determine number of select lines based on total input size
55 const int numSelectLines = calculateSelectLines(inputSize());
56 int numDataInputs = inputSize() - numSelectLines;
57
58 const int step = Constants::gridSize / 2; // 8
59
60 // Calculate element height to fit all data ports with padding
61 int dataPortsSpan = (numDataInputs - 1) * step * 2; // spacing between first and last port
62 int elementHeight = (std::max)(64, dataPortsSpan + step * 6);
63 // Snap to multiple of 16
64 elementHeight = ((elementHeight + 15) / 16) * 16;
65 int centerY = elementHeight / 2;
66
67 // Position data input ports on the left, centered vertically
68 int y = centerY - (numDataInputs - 1) * step;
69 for (int i = 0; i < numDataInputs; ++i) {
70 inputPort(i)->setPos(16, y);
71 inputPort(i)->setName(QString("In%1").arg(i));
72 y += step * 2;
73 }
74
75 // Position select lines at the bottom, spaced by gridSize to avoid overlap
76 int selectY = elementHeight - step;
77 for (int i = 0; i < numSelectLines; ++i) {
78 inputPort(numDataInputs + i)->setPos(32 + (i * step * 2), selectY);
79 inputPort(numDataInputs + i)->setName(QString("S%1").arg(i));
80 }
81
82 // Position output port on the right, centered
83 outputPort(0)->setPos(48, centerY);
84 outputPort(0)->setName("Out");
85
86 generatePixmap();
87}
88
89QRectF Mux::boundingRect() const
90{
91 return renderBodyBounds();
92}
93
94void Mux::generatePixmap()
95{
96 // The body is drawn as vectors in drawBody()/paint(); m_pixmap is kept only so the base
97 // pixmapCenter()/boundingRect() have the right size (its image content is never displayed).
98 const int height = static_cast<int>(renderBodyBounds().height());
99
100 QPixmap sizingPixmap(64, height);
101 sizingPixmap.fill(Qt::transparent);
102 m_appearance.setRenderPixmap(sizingPixmap);
103 update();
104}
105
106void Mux::drawBody(QPainter *painter)
107{
108 painter->save();
109 painter->setRenderHint(QPainter::Antialiasing);
110 // boundingRect()'s top-left may be negative when ports extend past the 64×64 body; align the
111 // local origin with it so the body lands exactly where the old rasterised pixmap was blitted.
112 painter->translate(boundingRect().topLeft());
113
114 const int height = pixmap().rect().height();
115
116 // Trapezoid body: wider on left (input side), narrower on right (output side)
117 const int bodyTop = 8;
118 const int bodyBottom = height - 10;
119 const int rightInset = 8; // fixed inset for the narrower right side
120
121 // Outer dark shape
122 painter->setBrush(QColor(38, 38, 38));
123 painter->setPen(QPen(QColor(38, 38, 38), 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
124
125 QPolygonF outer;
126 outer << QPointF(22, bodyTop)
127 << QPointF(22, bodyBottom)
128 << QPointF(42, bodyBottom - rightInset)
129 << QPointF(42, bodyTop + rightInset);
130 painter->drawPolygon(outer);
131
132 // Inner white fill
133 painter->setBrush(QColor(252, 252, 252));
134 painter->setPen(QPen(QColor(252, 252, 252), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
135
136 const int fillInset = 4;
137 QPolygonF inner;
138 inner << QPointF(22 + fillInset, bodyTop + fillInset)
139 << QPointF(22 + fillInset, bodyBottom - fillInset)
140 << QPointF(42 - fillInset, bodyBottom - rightInset - fillInset + 2)
141 << QPointF(42 - fillInset, bodyTop + rightInset + fillInset - 2);
142 painter->drawPolygon(inner);
143
144 painter->restore();
145}
146
147void Mux::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
148{
149 Q_UNUSED(widget)
150 Q_UNUSED(option)
151
152 if (isSelected()) {
153 painter->save();
154 painter->setBrush(m_appearance.selectionBrush());
155 painter->setPen(QPen(m_appearance.selectionPen(), 0.5, Qt::SolidLine));
156 painter->drawRoundedRect(boundingRect(), 5, 5);
157 painter->restore();
158 }
159
160 // Draw the body as vectors (crisp at any zoom) rather than blitting a fixed-resolution pixmap.
161 drawBody(painter);
162}
163
165{
167 return;
168 }
169
170 // Calculate select lines from current input count
171 const int numSelectLines = calculateSelectLines(inputSize());
172 int numDataInputs = inputSize() - numSelectLines;
173
174 // If any select line is Unknown/Error, the output is indeterminate
175 for (int i = 0; i < numSelectLines; i++) {
176 const Status sel = simInputs().at(numDataInputs + i);
177 if (sel == Status::Unknown || sel == Status::Error) {
178 setOutputValue(sel);
179 return;
180 }
181 }
182
183 const int selectValue = decodeSelectValue(numDataInputs, numSelectLines);
184
185 // With a non-power-of-two data width the select lines can encode values
186 // that address no data input (e.g. 5 data inputs, 3 select lines, select
187 // = 6). Routing is indeterminate — answer Unknown instead of reading a
188 // select line back as data or indexing past the input vector.
189 if (selectValue >= numDataInputs) {
190 setOutputValue(Status::Unknown);
191 return;
192 }
193
194 setOutputValue(simInputs().at(selectValue));
195}
196
197int Mux::calculateSelectLines(int totalInputs)
198{
199 int k = 1;
200 while ((1 << k) < totalInputs - k) {
201 k++;
202 }
203 return k;
204}
Shared numeric constants used across layers.
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
Graphic element for the Multiplexer (MUX).
Port classes: Port (base), InputPort, and OutputPort.
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...
InputPort * inputPort(const int index=0) const
Returns the input port at index (default 0).
OutputPort * outputPort(const int index=0) const
Returns the output port at index (default 0).
Graphical representation of a multiplexer (2^N-to-1).
Definition Mux.h:20
void updatePortsProperties() override
Recalculates port positions for the current port count.
Definition Mux.cpp:52
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
Definition Mux.cpp:147
void updateLogic() override
Routes the data input selected by the select lines to the output.
Definition Mux.cpp:164
Mux(QGraphicsItem *parent=nullptr)
Constructs the element with optional parent.
Definition Mux.cpp:46
QRectF boundingRect() const override
Definition Mux.cpp:89
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 const bool registered
Definition Mux.cpp:39
static constexpr ElementConstraints constraints
Definition Mux.cpp:17
static ElementMetadata metadata()
Definition Mux.cpp:28
Self-registering element information trait.
Compile-time-registered properties for one element type.