wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
DolphinModelBuilder.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 <algorithm>
7
8#include <QCoreApplication>
9
10#include "App/Core/Common.h"
14#include "App/Scene/Scene.h"
15
17
18namespace {
19
24template <typename Elements, typename PortCount>
25void appendRows(QVector<Row> &rows, const Elements &elements, const RowKind kind, const PortCount &portCount)
26{
27 for (auto *elm : elements) {
28 QString label = elm->label();
29
30 // Fall back to the element type name when the user hasn't given it a label
31 if (label.isEmpty()) {
32 label = ElementFactory::translatedName(elm->elementType());
33 }
34
35 const int ports = portCount(elm);
36 for (int port = 0; port < ports; ++port) {
37 // Multi-port elements (e.g. bus inputs, multi-bit displays) get indexed labels
38 const QString rowLabel = (ports > 1) ? label + "[" + QString::number(port) + "]" : label;
39 rows.append(Row{elm, port, kind, rowLabel});
40 }
41 }
42} // LCOV_EXCL_LINE -- compiler-generated cleanup for an exception path appendRows() never takes (both template instantiations)
43
45QStringList labelsOf(const QVector<Row> &rows, const RowKind kind)
46{
47 QStringList labels;
48 for (const auto &row : rows) {
49 if (row.kind == kind) {
50 labels.append(row.label);
51 }
52 }
53 return labels;
54} // LCOV_EXCL_LINE -- compiler-generated QStringList cleanup for an exception path labelsOf() never takes
55
56} // namespace
57
59{
60 Signals result;
61
62 const auto elements = scene->elements();
63
64 if (elements.isEmpty()) {
65 throw PANDACEPTION_WITH_CONTEXT("BewavedDolphin", "The circuit is empty. Add input and output elements to generate a waveform.");
66 }
67
68 for (auto *elm : elements) {
69 // Not reachable: Scene::elements() (the only real caller) already filters to items
70 // whose type() == GraphicElement::Type via qgraphicsitem_cast, so this can't be null
71 // or the wrong type here — kept as a defensive guard against a future caller passing
72 // a hand-built element list.
73 if (!elm || (elm->type() != GraphicElement::Type)) {
74 continue; // LCOV_EXCL_LINE
75 }
76
77 if (elm->elementGroup() == ElementGroup::Input) {
78 result.inputs.append(qobject_cast<GraphicElementInput *>(elm));
79 }
80
81 if (elm->elementGroup() == ElementGroup::Output) {
82 result.outputs.append(elm);
83 }
84 }
85
86 // Stable sort by label so the waveform table ordering is deterministic and
87 // matches what the user expects from the label names they assigned
88 std::stable_sort(result.inputs.begin(), result.inputs.end(), [](const auto &elm1, const auto &elm2) {
89 return QString::compare(elm1->label(), elm2->label(), Qt::CaseInsensitive) < 0;
90 });
91
92 std::stable_sort(result.outputs.begin(), result.outputs.end(), [](const auto &elm1, const auto &elm2) {
93 return QString::compare(elm1->label(), elm2->label(), Qt::CaseInsensitive) < 0;
94 });
95
96 if (result.inputs.isEmpty() && result.outputs.isEmpty()) {
97 throw PANDACEPTION_WITH_CONTEXT("BewavedDolphin", "The circuit has no input or output elements. Add at least one input (e.g. Switch) and one output (e.g. LED) to generate a waveform.");
98 }
99
100 if (result.inputs.isEmpty()) {
101 throw PANDACEPTION_WITH_CONTEXT("BewavedDolphin", "The circuit has no input elements. Add at least one input (e.g. Switch, Button, or Clock) to generate a waveform.");
102 }
103
104 if (result.outputs.isEmpty()) {
105 throw PANDACEPTION_WITH_CONTEXT("BewavedDolphin", "The circuit has no output elements. Add at least one output (e.g. LED or Display) to generate a waveform.");
106 }
107
108 // Rows first -- they are the source of truth; the label lists are projections of them.
109 appendRows(result.rows, result.inputs, RowKind::Input, [](const GraphicElementInput *e) { return e->outputSize(); });
110 appendRows(result.rows, result.outputs, RowKind::Output, [](const GraphicElement *e) { return e->inputSize(); });
111
112 result.inputLabels = labelsOf(result.rows, RowKind::Input);
113 result.outputLabels = labelsOf(result.rows, RowKind::Output);
114 // Derived, not accumulated separately: the input rows ARE inputLabels, so counting ports in
115 // the collection loop would be a second computation of the same quantity by different code --
116 // the duplication this row layout exists to remove.
117 result.inputPorts = static_cast<int>(result.inputLabels.size());
118
119 return result;
120}
121
122} // namespace DolphinModelBuilder
Common logging utilities, the Pandaception error type, and helper macros.
#define PANDACEPTION_WITH_CONTEXT(context, msg,...)
Definition Common.h:102
DolphinModelBuilder: maps a circuit scene's I/O elements to waveform signal rows.
Singleton factory for all circuit element types.
Abstract base class for user-controllable input elements.
Abstract base class for all graphical circuit elements.
Main circuit editing scene with undo/redo and user interaction.
static QString translatedName(const ElementType type)
Returns the translated human-readable name for type.
Abstract base for all interactive input elements (switches, buttons, clocks, etc.).
Abstract base class for all graphical circuit elements in wiRedPanda.
Main circuit editing scene.
Definition Scene.h:56
const QVector< GraphicElement * > elements() const
Returns all graphic elements in the scene.
Definition Scene.cpp:336
Collects and orders the input/output elements of a circuit scene and derives the waveform table's row...
Signals collect(Scene *scene)
RowKind
Which side of the table a row belongs to.
One waveform table row: exactly one PORT of one element.
The input/output elements (label-sorted) and the row layout for a waveform table.
QVector< GraphicElement * > outputs
Output elements, sorted by label.
QStringList inputLabels
Row label per input port (indexed for multi-port).
QVector< GraphicElementInput * > inputs
Input elements, sorted by label.
int inputPorts
Total input rows; derived from rows.