wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
TruthTable.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 <QGraphicsSceneMouseEvent>
7#include <QPainter>
8#include <QSvgRenderer>
9
10#include "App/Core/Common.h"
11#include "App/Core/Constants.h"
16#include "App/IO/VersionInfo.h"
17#include "App/Wiring/Port.h"
18
19template<>
22 .type = ElementType::TruthTable,
23 .group = ElementGroup::IC,
24 .minInputSize = 2,
25 .maxInputSize = 8,
26 .minOutputSize = 1,
27 .maxOutputSize = 8,
28 .canChangeAppearance = true,
29 .hasLabel = true,
30 .hasTruthTable = true,
31 };
32 static_assert(validate(constraints));
33
35 {
37 meta.pixmapPath = []{ return QStringLiteral(":/Components/Logic/truthtable-rotated.svg"); };
38 meta.titleText = QT_TRANSLATE_NOOP("TruthTable", "TRUTH TABLE");
39 meta.translatedName = QT_TRANSLATE_NOOP("TruthTable", "Truth Table");
40 meta.trContext = "TruthTable";
41 meta.defaultAppearances = QStringList({":/Components/Logic/truthtable-rotated.svg"});
42 return meta;
43 }
44
45 static inline const bool registered = []() {
47 ElementFactory::registerCreator(constraints.type, [] { return new TruthTable(); });
48 return true;
49 }();
50};
51
52TruthTable::TruthTable(QGraphicsItem *parent)
54{
55 // 2048 bits = 256 rows × 8 output columns (max: 8 inputs × 8 outputs).
56 // Laid out as [row0_out0, row0_out1, ..., row0_out7, row1_out0, ...].
57 // All outputs default to 0 (all-false table).
58 m_key.resize(2048);
59 m_key.fill(0);
61}
62
64{
65 int index = 0;
66 // step = 8px (half the 16px grid), giving ports a 16px pitch (every other grid line)
67 const int step = Constants::gridSize / 2;
68
69 if (!inputs().isEmpty()) {
70 // Center the input port column vertically within the 64px-minimum body height.
71 // Formula: start at body centre (32) minus half the total column height, then
72 // add one step back so the first port lands at the right offset.
73 // Total height of n ports at 2*step pitch = n * 2*step; half = n*step.
74 // e.g. 4 inputs → y_start = 32 - 4*8 + 8 = 8; ports at y=8,24,40,56
75 int y = 32 - (static_cast<int>(inputs().size()) * step) + step;
76
77 for (auto *port : inputs()) {
78
79 if (!rotatesGraphic()) {
80 port->setRotation(0);
81 }
82
83 port->setPos(0, y);
84
85 y += step * 2;
86
87 // Inputs are labeled A, B, C, ... (alphabetically, matching truth table convention)
88 port->setName(QChar::fromLatin1(static_cast<char>('A' + index)));
89 ++index;
90 }
91 }
92
93 index = 0;
94
95 if (!outputs().isEmpty()) {
96 // Same centering formula as inputs; output column is on the right edge (x=64)
97 int y = 32 - (static_cast<int>(outputs().size()) * step) + step;
98
99 for (auto *port : outputs()) {
100
101 if (!rotatesGraphic()) {
102 port->setRotation(0);
103 }
104
105 port->setPos(64, y);
106
107 y += step * 2;
108
109 // Outputs are labeled S0, S1, ... to distinguish them from input names
110 port->setName("S" + QString::number(index));
111 ++index;
112 }
113 }
114
115 setLabelAnchor(QPointF(30, renderBodyBounds().bottom() + 5));
116
117 generatePixmap();
118}
119
122static QSvgRenderer &truthTableLogoRenderer()
123{
124 static QSvgRenderer renderer(QStringLiteral(":/Components/Logic/truthtable-rotated.svg"));
125 return renderer;
126}
127
129{
130 return renderBodyBounds();
131}
132
133void TruthTable::generatePixmap()
134{
135 // The TruthTable renders a custom IC-style body, now drawn as vectors in drawBody()/paint().
136 // m_pixmap is kept only so the base pixmapCenter()/boundingRect() have the right size (its image
137 // content is never displayed); a transparent pixmap of the body footprint is enough. It is
138 // regenerated whenever the port count changes because the body height grows with the port layout.
139 const QSize size = renderBodyBounds().size().toSize();
140 QPixmap sizingPixmap(size);
141 sizingPixmap.fill(Qt::transparent);
142 m_appearance.setRenderPixmap(sizingPixmap);
143 GraphicElement::update();
144}
145
146void TruthTable::drawBody(QPainter *painter)
147{
148 painter->save();
149 painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true);
150 // boundingRect()'s top-left may be negative when ports extend past the 64×64 body; align the
151 // local origin with it so the body lands exactly where the old rasterised pixmap was blitted.
152 painter->translate(boundingRect().topLeft());
153 // The body footprint is the (correctly-sized) m_pixmap rect — exactly the area the old raster
154 // occupied — so the geometry is reproduced 1:1 at any zoom.
155 const QRectF bounds(pixmap().rect());
156
157 // Main body: mid-gray rounded rectangle inset 7px on each side (14px total) so port connector
158 // dots sit on top of the body border rather than floating in empty space.
159 painter->setBrush(QColor(126, 126, 126));
160 painter->setPen(QPen(QBrush(QColor(78, 78, 78)), 0.5, Qt::SolidLine));
161 const QRectF finalRect(QPointF(7, 0), QSizeF(bounds.width() - 14, bounds.height()));
162 painter->drawRoundedRect(finalRect, 3, 3);
163
164 // Centre the truth-table icon inside the body, rendered as vectors at its native size.
165 // The icon is decoration, like the baked-in SVG pin text: counter-orient it about its own
166 // centre (rotate outer, flip inner — the inverse of the item's Flip∘Rotate) so it reads
167 // upright at any element orientation.
168 QSvgRenderer &logo = truthTableLogoRenderer();
169 const QSizeF logoSize = logo.defaultSize();
170 const QRectF logoRect(finalRect.center() - QPointF(logoSize.width() / 2, logoSize.height() / 2), logoSize);
171 painter->save();
172 painter->translate(logoRect.center());
173 painter->rotate(-rotation());
174 painter->scale(isFlippedX() ? -1 : 1, isFlippedY() ? -1 : 1);
175 painter->translate(-logoRect.center());
176 logo.render(painter, logoRect);
177 painter->restore();
178
179 // Shadow strip at the bottom of the body for a subtle 3-D depth effect.
180 painter->setBrush(QColor(78, 78, 78));
181 painter->setPen(QPen(QBrush(QColor(78, 78, 78)), 0.5, Qt::SolidLine));
182 QRectF shadowRect(finalRect.bottomLeft(), finalRect.bottomRight());
183 shadowRect.adjust(0, -3, 0, 0);
184 painter->drawRoundedRect(shadowRect, 3, 3);
185
186 painter->restore();
187}
188
189void TruthTable::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
190{
191 Q_UNUSED(widget)
192 Q_UNUSED(option)
193
194 if (isSelected()) {
195 painter->save();
196 painter->setBrush(m_appearance.selectionBrush());
197 painter->setPen(QPen(m_appearance.selectionPen(), 0.5, Qt::SolidLine));
198 // Expand the highlight rect to cover any ports that extend outside the 64x64 body
199 painter->drawRoundedRect(boundingRect(), 5, 5);
200 painter->restore();
201 }
202
203 // Draw the body as vectors (crisp at any zoom) rather than blitting a fixed-resolution pixmap.
204 drawBody(painter);
205}
206
207void TruthTable::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
208{
209 event->accept();
211}
212
213QBitArray &TruthTable::key()
214{
215 return m_key;
216}
217
218void TruthTable::setkey(const QBitArray &key)
219{
220 m_key = key;
221 // updateLogic() indexes the key at 256*output + row (up to bit 2047) and
222 // ToggleTruthTableOutputCommand toggles bits in place, so the key must
223 // hold exactly 2048 bits regardless of what a (possibly corrupt or
224 // crafted) .panda file supplied — resize pads with zeros or truncates.
225 m_key.resize(2048);
226}
227
228void TruthTable::save(QDataStream &stream, SerializationOptions options) const
229{
230 GraphicElement::save(stream, options);
231 QMap<QString, QVariant> map;
232 map.insert("key", m_key);
233 stream << map;
234}
235
236void TruthTable::load(QDataStream &stream, SerializationContext &context)
237{
238 GraphicElement::load(stream, context);
239
241 // Truth-table key (the output bit-array) was first serialized in v4.2
242 QMap<QString, QVariant> map = Serialization::readBoundedMetadata(stream);
243
244 if (map.contains("key")) {
245 setkey(map.value("key").toBitArray());
246 }
247 }
248}
249
251{
253 return;
254 }
255
256 // If any input is Unknown/Error, the row cannot be determined
257 for (const auto s : simInputs()) {
258 if (s == Status::Unknown || s == Status::Error) {
259 for (int i = 0; i < outputSize(); ++i) {
260 setOutputValue(i, s);
261 }
262 return;
263 }
264 }
265
266 // The row index is the inputs read MSB-first (input 0 is the most
267 // significant bit) — computed once with integer ops instead of building
268 // and re-parsing a binary QString per output per tick.
269 quint32 pos = 0;
270 for (const auto s : simInputs()) {
271 pos = (pos << 1) | ((s == Status::Active) ? 1U : 0U);
272 }
273
274 for (int i = 0; i < outputSize(); ++i) {
275 const bool result = m_key.at(static_cast<qsizetype>(256 * i) + static_cast<qsizetype>(pos));
276 setOutputValue(i, result);
277 }
278}
Common logging utilities, the Pandaception error type, and helper macros.
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
Port classes: Port (base), InputPort, and OutputPort.
Deserialization/serialization context structs passed through load()/save() call chains.
Circuit and waveform file serialization/deserialization utilities.
static QSvgRenderer & truthTableLogoRenderer()
Graphic element for a user-programmable truth table.
Named version predicates for file-format compatibility checks.
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.
ElementAppearance m_appearance
qreal rotation() const
Returns the current rotation angle of this element in degrees.
bool isFlippedY() const
Returns true if this element is mirrored along the Y axis (vertical flip).
void setLabelAnchor(const QPointF &pos)
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...
virtual void load(QDataStream &stream, SerializationContext &context)
Loads the graphic element through a binary data stream.
int outputSize() const
Returns the current number of output ports.
virtual void save(QDataStream &stream, SerializationOptions options) const
bool isFlippedX() const
Returns true if this element is mirrored along the X axis (horizontal flip).
const QVector< OutputPort * > & outputs() const
Returns a const reference to the vector of all output ports.
bool rotatesGraphic() const
const QVector< InputPort * > & inputs() const
Returns a const reference to the vector of all input ports.
static QMap< QString, QVariant > readBoundedMetadata(QDataStream &stream)
Reads the file-level metadata QMap<QString,QVariant> from stream without calling QList::reserve() wit...
Programmable truth-table element with configurable inputs and outputs.
Definition TruthTable.h:20
void setkey(const QBitArray &key)
Sets the truth-table output bit-array to key.
void updatePortsProperties() override
Resizes the truth-table key when input or output count changes.
void requestOpenTruthTableEditor()
Emitted on double-click to request opening the truth table editor.
QBitArray & key()
Returns a reference to the truth-table output bit-array.
QRectF boundingRect() const override
void load(QDataStream &stream, SerializationContext &context) override
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
void save(QDataStream &stream, SerializationOptions options) const override
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override
void updateLogic() override
Looks up the current input pattern in the truth table and drives outputs.
TruthTable(QGraphicsItem *parent=nullptr)
Constructs the element with optional parent.
constexpr int gridSize
Scene grid unit in pixels (elements snap to gridSize/2).
Definition Constants.h:12
bool hasTruthTableData(const QVersionNumber &v)
V4.2: TruthTable output data stored in file.
Definition VersionInfo.h:63
Compile-time-validatable subset of ElementMetadata.
Definition ElementInfo.h:21
static ElementMetadata metadata()
static constexpr ElementConstraints constraints
static const bool registered
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...