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 } // LCOV_EXCL_LINE — recurring pattern 1: compiler-generated cleanup for the returned ElementMetadata's QString/QStringList members, never reached after the return above.
44
45 static inline const bool registered = []() {
47 ElementFactory::registerCreator(constraints.type, [] { return new TruthTable(); });
48 return true;
49 }();
50};
51
52namespace {
53
56constexpr qsizetype kTruthTableRows = qsizetype{1} << ElementInfo<TruthTable>::constraints.maxInputSize;
57
61constexpr qsizetype kTruthTableKeyBits = kTruthTableRows * ElementInfo<TruthTable>::constraints.maxOutputSize;
62
63static_assert(kTruthTableKeyBits == 2048,
64 "The .panda format stores this QBitArray verbatim and setkey() resizes every loaded "
65 "key to it, so the size is part of the file format. Raising maxInputSize or "
66 "maxOutputSize silently changes what is written and what older files decode to -- "
67 "handle it as a format change, not a constant edit.");
68
69} // namespace
70
71TruthTable::TruthTable(QGraphicsItem *parent)
73{
74 // Laid out as [out0_row0..out0_rowN, out1_row0..out1_rowN, ...].
75 // All outputs default to 0 (all-false table).
76 m_key.resize(kTruthTableKeyBits);
77 m_key.fill(0);
79}
80
82{
83 int index = 0;
84 // step = 8px (half the 16px grid), giving ports a 16px pitch (every other grid line)
85 const int step = Constants::gridSize / 2;
86
87 if (!inputs().isEmpty()) {
88 // Center the input port column vertically within the 64px-minimum body height.
89 // Formula: start at body centre (32) minus half the total column height, then
90 // add one step back so the first port lands at the right offset.
91 // Total height of n ports at 2*step pitch = n * 2*step; half = n*step.
92 // e.g. 4 inputs → y_start = 32 - 4*8 + 8 = 8; ports at y=8,24,40,56
93 int y = 32 - (static_cast<int>(inputs().size()) * step) + step;
94
95 for (auto *port : inputs()) {
96
97 // Unreachable: rotatesGraphic() reads from this type's compile-time metadata,
98 // which TruthTable's ElementInfo never overrides away from the default (true).
99 if (!rotatesGraphic()) { // LCOV_EXCL_LINE
100 port->setRotation(0); // LCOV_EXCL_LINE
101 } // LCOV_EXCL_LINE
102
103 port->setPos(0, y);
104
105 y += step * 2;
106
107 // Inputs are labeled A, B, C, ... (alphabetically, matching truth table convention)
108 port->setName(QChar::fromLatin1(static_cast<char>('A' + index)));
109 ++index;
110 }
111 }
112
113 index = 0;
114
115 if (!outputs().isEmpty()) {
116 // Same centering formula as inputs; output column is on the right edge (x=64)
117 int y = 32 - (static_cast<int>(outputs().size()) * step) + step;
118
119 for (auto *port : outputs()) {
120
121 // Unreachable for the same reason as the input-port loop above.
122 if (!rotatesGraphic()) { // LCOV_EXCL_LINE
123 port->setRotation(0); // LCOV_EXCL_LINE
124 } // LCOV_EXCL_LINE
125
126 port->setPos(64, y);
127
128 y += step * 2;
129
130 // Outputs are labeled S0, S1, ... to distinguish them from input names
131 port->setName("S" + QString::number(index));
132 ++index;
133 }
134 }
135
136 setLabelAnchor(QPointF(30, renderBodyBounds().bottom() + 5));
137
138 generatePixmap();
139}
140
143static QSvgRenderer &truthTableLogoRenderer()
144{
145 static QSvgRenderer renderer(QStringLiteral(":/Components/Logic/truthtable-rotated.svg"));
146 return renderer;
147}
148
150{
151 return renderBodyBounds();
152}
153
154void TruthTable::generatePixmap()
155{
156 // The TruthTable renders a custom IC-style body, now drawn as vectors in drawBody()/paint().
157 // m_pixmap is kept only so the base pixmapCenter()/boundingRect() have the right size (its image
158 // content is never displayed); a transparent pixmap of the body footprint is enough. It is
159 // regenerated whenever the port count changes because the body height grows with the port layout.
160 const QSize size = renderBodyBounds().size().toSize();
161 QPixmap sizingPixmap(size);
162 sizingPixmap.fill(Qt::transparent);
163 m_appearance.setRenderPixmap(sizingPixmap);
164 GraphicElement::update();
165}
166
167void TruthTable::drawBody(QPainter *painter)
168{
169 painter->save();
170 painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing, true);
171 // boundingRect()'s top-left may be negative when ports extend past the 64×64 body; align the
172 // local origin with it so the body lands exactly where the old rasterised pixmap was blitted.
173 painter->translate(boundingRect().topLeft());
174 // The body footprint is the (correctly-sized) m_pixmap rect — exactly the area the old raster
175 // occupied — so the geometry is reproduced 1:1 at any zoom.
176 const QRectF bounds(pixmap().rect());
177
178 // Main body: mid-gray rounded rectangle inset 7px on each side (14px total) so port connector
179 // dots sit on top of the body border rather than floating in empty space.
180 painter->setBrush(QColor(126, 126, 126));
181 painter->setPen(QPen(QBrush(QColor(78, 78, 78)), 0.5, Qt::SolidLine));
182 const QRectF finalRect(QPointF(7, 0), QSizeF(bounds.width() - 14, bounds.height()));
183 painter->drawRoundedRect(finalRect, 3, 3);
184
185 // Centre the truth-table icon inside the body, rendered as vectors at its native size.
186 // The icon is decoration, like the baked-in SVG pin text: counter-orient it about its own
187 // centre (rotate outer, flip inner — the inverse of the item's Flip∘Rotate) so it reads
188 // upright at any element orientation.
189 QSvgRenderer &logo = truthTableLogoRenderer();
190 const QSizeF logoSize = logo.defaultSize();
191 const QRectF logoRect(finalRect.center() - QPointF(logoSize.width() / 2, logoSize.height() / 2), logoSize);
192 painter->save();
193 painter->translate(logoRect.center());
194 painter->rotate(-rotation());
195 painter->scale(isFlippedX() ? -1 : 1, isFlippedY() ? -1 : 1);
196 painter->translate(-logoRect.center());
197 logo.render(painter, logoRect);
198 painter->restore();
199
200 // Shadow strip at the bottom of the body for a subtle 3-D depth effect.
201 painter->setBrush(QColor(78, 78, 78));
202 painter->setPen(QPen(QBrush(QColor(78, 78, 78)), 0.5, Qt::SolidLine));
203 QRectF shadowRect(finalRect.bottomLeft(), finalRect.bottomRight());
204 shadowRect.adjust(0, -3, 0, 0);
205 painter->drawRoundedRect(shadowRect, 3, 3);
206
207 painter->restore();
208}
209
210void TruthTable::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
211{
212 Q_UNUSED(widget)
213 Q_UNUSED(option)
214
215 if (isSelected()) {
216 painter->save();
217 painter->setBrush(m_appearance.selectionBrush());
218 painter->setPen(QPen(m_appearance.selectionPen(), 0.5, Qt::SolidLine));
219 // Expand the highlight rect to cover any ports that extend outside the 64x64 body
220 painter->drawRoundedRect(boundingRect(), 5, 5);
221 painter->restore();
222 }
223
224 // Draw the body as vectors (crisp at any zoom) rather than blitting a fixed-resolution pixmap.
225 drawBody(painter);
226}
227
228void TruthTable::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
229{
230 event->accept();
232}
233
234QBitArray &TruthTable::key()
235{
236 return m_key;
237}
238
239void TruthTable::setkey(const QBitArray &key)
240{
241 m_key = key;
242 // updateLogic() indexes the key at kTruthTableRows*output + row (up to the last bit) and
243 // ToggleTruthTableOutputCommand toggles bits in place, so the key must hold exactly
244 // kTruthTableKeyBits bits regardless of what a (possibly corrupt or crafted) .panda file
245 // supplied — resize pads with zeros or truncates.
246 m_key.resize(kTruthTableKeyBits);
247}
248
249void TruthTable::save(QDataStream &stream, SerializationOptions options) const
250{
251 GraphicElement::save(stream, options);
252 QMap<QString, QVariant> map;
253 map.insert("key", m_key);
254 stream << map;
255}
256
257void TruthTable::load(QDataStream &stream, SerializationContext &context)
258{
259 GraphicElement::load(stream, context);
260
262 // Truth-table key (the output bit-array) was first serialized in v4.2
263 QMap<QString, QVariant> map = Serialization::readBoundedMetadata(stream);
264
265 if (map.contains("key")) {
266 setkey(map.value("key").toBitArray());
267 }
268 }
269}
270
272{
274 return;
275 }
276
277 // If any input is Unknown/Error, the row cannot be determined
278 for (const auto s : simInputs()) {
279 if (s == Status::Unknown || s == Status::Error) {
280 for (int i = 0; i < outputSize(); ++i) {
281 setOutputValue(i, s);
282 }
283 return;
284 }
285 }
286
287 // The row index is the inputs read MSB-first (input 0 is the most
288 // significant bit) — computed once with integer ops instead of building
289 // and re-parsing a binary QString per output per tick.
290 quint32 pos = 0;
291 for (const auto s : simInputs()) {
292 pos = (pos << 1) | ((s == Status::Active) ? 1U : 0U);
293 }
294
295 for (int i = 0; i < outputSize(); ++i) {
296 const bool result = m_key.at(kTruthTableRows * i + static_cast<qsizetype>(pos));
297 setOutputValue(i, result);
298 }
299}
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...