9#include <QCoreApplication>
28void storeIds(
const QList<QGraphicsItem *> &items, QList<int> &ids)
30 ids.reserve(items.size());
32 for (
auto *item : items) {
33 if (
auto *itemId =
dynamic_cast<ItemWithId *
>(item)) {
34 ids.append(itemId->id());
39void storeOtherIds(
const QList<QGraphicsItem *> &connections,
const QList<int> &ids, QList<int> &otherIds)
44 for (
auto *item : connections) {
45 if (
auto *conn = qgraphicsitem_cast<Connection *>(item)) {
46 if (
auto *port1 = conn->startPort(); port1 && port1->graphicElement() && !ids.contains(port1->graphicElement()->id())) {
47 otherIds.append(port1->graphicElement()->id());
50 if (
auto *port2 = conn->endPort(); port2 && port2->graphicElement() && !ids.contains(port2->graphicElement()->id())) {
51 otherIds.append(port2->graphicElement()->id());
57const QList<QGraphicsItem *>
loadList(
const QList<QGraphicsItem *> &items, QList<int> &ids, QList<int> &otherIds)
60 QList<QGraphicsItem *> elements;
62 for (
auto *item : items) {
64 if (!elements.contains(item)) {
65 elements.append(item);
73 QList<QGraphicsItem *> connections;
75 for (
auto *item : elements) {
76 if (
auto *elm = qgraphicsitem_cast<GraphicElement *>(item)) {
77 for (
auto *port : elm->inputs()) {
78 for (
auto *conn : port->connections()) {
79 if (!connections.contains(conn)) {
80 connections.append(conn);
85 for (
auto *port : elm->outputs()) {
86 for (
auto *conn : port->connections()) {
87 if (!connections.contains(conn)) {
88 connections.append(conn);
96 for (
auto *item : items) {
98 if (!connections.contains(item)) {
99 connections.append(item);
105 storeIds(elements + connections, ids);
108 return elements + connections;
113 QList<QGraphicsItem *> items;
114 items.reserve(ids.size());
116 for (
const int id : ids) {
117 if (
auto *item =
dynamic_cast<QGraphicsItem *
>(scene->
itemById(
id))) {
122 if (items.size() != ids.size()) {
131 QList<GraphicElement *> items;
132 items.reserve(ids.size());
134 for (
const int id : ids) {
140 if (items.size() != ids.size()) {
157void saveItems(
Scene *scene, QByteArray &itemData,
const QList<QGraphicsItem *> &items,
const QList<int> &otherIds)
160 QDataStream stream(&itemData, QIODevice::WriteOnly);
165 for (
auto *elm : others) {
174 for (
auto *item : items) {
175 if (item->scene() != scene) {
180 item->setSelected(
true);
185const QList<QGraphicsItem *>
loadItems(
Scene *scene, QByteArray &itemData,
const QList<int> &ids, QList<int> &otherIds)
187 if (itemData.isEmpty()) {
191 QDataStream stream(&itemData, QIODevice::ReadOnly);
194 QHash<quint64, Port *> portMap;
198 elm->load(stream, context);
205 if (items.size() != ids.size()) {
208 throw PANDACEPTION_WITH_CONTEXT(
"commands",
"One or more elements were not found on scene. Expected %1, found %2.",
static_cast<int>(ids.size()),
static_cast<int>(items.size()));
212 for (
int i = 0; i < items.size(); ++i) {
213 if (
auto *itemId =
dynamic_cast<ItemWithId *
>(items.at(i))) {
225 for (
auto it = items.rbegin(); it != items.rend(); ++it) {
232 bool isInput, QDataStream &stream,
Scene *scene)
235 for (
int port = fromPort; port < toPort; ++port) {
237 connCount +=
static_cast<int>(p->
connections().size());
241 for (
int port = fromPort; port < toPort; ++port) {
245 stream << conn->id();
247 conn->setStartPort(
nullptr);
248 conn->setEndPort(
nullptr);
258 : QUndoCommand(parent)
263 m_ids.append(elm->id());
273 : QUndoCommand(parent)
285 setText(tr(
"Add %1 elements").arg(items_.size()));
295 m_scene->setCircuitUpdateRequired();
303 m_scene->setCircuitUpdateRequired();
307 : QUndoCommand(parent)
311 setText(tr(
"Delete %1 elements").arg(items_.size()));
319 m_scene->setCircuitUpdateRequired();
329 m_scene->setCircuitUpdateRequired();
336 setText(tr(
"Rotate %1 degrees").arg(m_angle));
337 m_positions.reserve(items.size());
339 for (
auto *item : items) {
340 m_positions.append(item->pos());
341 item->setPos(item->pos());
350 for (
int i = 0; i <
elements.size(); ++i) {
352 elm->setRotation(elm->rotation() - m_angle);
353 elm->setPos(m_positions.at(i));
355 elm->setSelected(
true);
358 m_scene->setAutosaveRequired();
369 cx += elm->pos().x();
370 cy += elm->pos().y();
381 QTransform transform;
382 transform.translate(cx, cy);
383 transform.rotate(m_angle);
384 transform.translate(-cx, -cy);
387 elm->setPos(transform.map(elm->pos()));
388 elm->setRotation(elm->rotation() + m_angle);
391 m_scene->setAutosaveRequired();
396 , m_oldPositions(oldPositions)
398 m_newPositions.reserve(list.size());
400 for (
auto *elm : list) {
401 m_newPositions.append(elm->pos());
404 setText(tr(
"Move elements"));
411 for (
int i = 0; i <
elements.size(); ++i) {
412 elements.at(i)->setPos(m_oldPositions.at(i));
415 m_scene->setAutosaveRequired();
422 for (
int i = 0; i <
elements.size(); ++i) {
423 elements.at(i)->setPos(m_newPositions.at(i));
426 m_scene->setAutosaveRequired();
436 QDataStream stream(&m_newData, QIODevice::WriteOnly);
451 const auto newState = snapshotWirelessState();
452 if (!newState.isEmpty()) {
455 m_wirelessTopologyChange = wirelessStateDiffers(snapshotWirelessState(), newState);
459 setText(tr(
"Update %1 elements").arg(
elements.size()));
470 refreshRuntimeState();
478 refreshRuntimeState();
481QVector<UpdateCommand::WirelessState> UpdateCommand::snapshotWirelessState()
const
483 QVector<WirelessState> state;
486 if (elm && elm->hasWirelessMode()) {
487 state.append({elm, elm->wirelessMode(), elm->label()});
493bool UpdateCommand::wirelessStateDiffers(
const QVector<WirelessState> &before,
494 const QVector<WirelessState> &after)
500 if (before.size() != after.size()) {
503 for (
int i = 0; i < before.size(); ++i) {
504 const auto &b = before.at(i);
505 const auto &a = after.at(i);
506 if (b.element != a.element || b.mode != a.mode) {
509 if (b.label != a.label && a.mode != WirelessMode::None) {
516void UpdateCommand::refreshRuntimeState()
521 if (m_wirelessTopologyChange) {
522 m_scene->setCircuitUpdateRequired();
530 m_scene->setPropertyUpdateRequired();
533void UpdateCommand::loadData(QByteArray &itemData)
541 QDataStream stream(&itemData, QIODevice::ReadOnly);
544 QHash<quint64, Port *> portMap;
548 elm->load(stream, context);
549 elm->setSelected(
true);
554 : QUndoCommand(parent)
562 m_nodePos = mousePos - node->pixmapCenter();
564 qreal xV = qRound(m_nodePos.x() / gridSize) * gridSize;
565 qreal yV = qRound(m_nodePos.y() / gridSize) * gridSize;
566 m_nodePos = QPointF(xV, yV);
573 const int angle =
static_cast<int>(conn->
angle());
574 m_nodeAngle =
static_cast<int>(360 - 90 * (std::round(angle / 90.0)));
578 auto *endPort = conn->
endPort();
579 if (!startPort || !endPort) {
580 throw PANDACEPTION(
"Invalid connection ports in SplitCommand constructor");
584 auto *endElement = endPort->graphicElement();
585 if (!startElement || !endElement) {
586 throw PANDACEPTION(
"Invalid graphic elements in SplitCommand constructor");
589 m_elm1Id = startElement->
id();
590 m_elm2Id = endElement->id();
598 m_scene->addItem(conn2);
599 m_c2Id = conn2->id();
600 m_scene->removeItem(conn2);
604 m_scene->addItem(node);
605 m_nodeId = node->id();
606 m_scene->removeItem(node);
609 setText(tr(
"Wire split"));
624 if (!conn1 || !elm1 || !elm2) {
628 auto *endPort = conn1->endPort();
630 throw PANDACEPTION(
"Error: endPort is null in SplitCommand::redo()");
640 m_scene->updateItemId(conn2, m_c2Id);
645 m_scene->updateItemId(node, m_nodeId);
648 node->setPos(m_nodePos);
649 node->setRotation(m_nodeAngle);
652 conn2->setStartPort(node->outputPort());
653 conn2->setEndPort(endPort);
654 conn1->setEndPort(node->inputPort());
656 m_scene->addItem(node);
657 m_scene->addItem(conn2);
659 conn1->updatePosFromPorts();
660 conn2->updatePosFromPorts();
662 m_scene->setCircuitUpdateRequired();
676 if (!conn1 || !conn2 || !elm1 || !elm2 || !node) {
681 conn1->setEndPort(conn2->endPort());
683 conn1->updatePosFromPorts();
686 m_scene->removeItem(conn2);
687 m_scene->removeItem(node);
692 m_scene->setCircuitUpdateRequired();
704 m_types.append(oldElm->elementType());
707 setText(tr(
"Morph %1 elements to %2").arg(
elements.size()).arg(
elements.constFirst()->objectName()));
710void MorphCommand::restoreDeletedConnections(
const QList<DeletedConnectionInfo> &deleted)
714 for (
const auto &info : deleted) {
717 if (!morphedElm || !otherElm) {
721 auto *conn =
new Connection();
723 conn->setStartPort(otherElm->outputPort(info.otherPortIndex));
724 conn->setEndPort(morphedElm->inputPort(info.portIndex));
726 conn->setStartPort(morphedElm->outputPort(info.portIndex));
727 conn->setEndPort(otherElm->inputPort(info.otherPortIndex));
731 m_scene->updateItemId(conn, info.connectionId);
743 decltype(newElms) oldElms;
744 oldElms.reserve(
m_ids.size());
746 for (
int i = 0; i <
m_ids.size(); ++i) {
750 m_deletedConnectionsOnUndo.clear();
751 transferConnections(newElms, oldElms, &m_deletedConnectionsOnUndo);
754 restoreDeletedConnections(m_deletedConnections);
756 m_scene->setCircuitUpdateRequired();
764 decltype(oldElms) newElms;
765 newElms.reserve(
m_ids.size());
767 for (
int i = 0; i <
m_ids.size(); ++i) {
771 m_deletedConnections.clear();
772 transferConnections(oldElms, newElms, &m_deletedConnections);
777 restoreDeletedConnections(m_deletedConnectionsOnUndo);
779 m_scene->setCircuitUpdateRequired();
782void MorphCommand::transferConnections(
const QList<GraphicElement *> &from,
const QList<GraphicElement *> &to,
783 QList<DeletedConnectionInfo> *deleted)
785 for (
int elm = 0; elm < from.size(); ++elm) {
786 auto *oldElm = from.at(elm);
787 auto *newElm = to.at(elm);
789 newElm->setInputSize(oldElm->inputSize());
790 newElm->setPos(oldElm->pos());
794 if ((oldElm->elementType() == ElementType::Not) && (newElm->elementType() == ElementType::Node)) {
795 newElm->moveBy(16, 16);
798 if ((oldElm->elementType() == ElementType::Node) && (newElm->elementType() == ElementType::Not)) {
799 newElm->moveBy(-16, -16);
804 if (newElm->rotatesGraphic() && oldElm->rotatesGraphic()) {
805 newElm->setRotation(oldElm->rotation());
808 if (newElm->hasLabel() && oldElm->hasLabel()) {
809 newElm->setLabel(oldElm->label());
812 if (newElm->hasColors() && oldElm->hasColors()) {
813 newElm->setColor(oldElm->color());
816 if (newElm->hasFrequency() && oldElm->hasFrequency()) {
817 newElm->setFrequency(oldElm->frequency());
820 if (newElm->hasTrigger() && oldElm->hasTrigger()) {
821 newElm->setTrigger(oldElm->trigger());
826 newElm->setFlippedX(oldElm->isFlippedX());
827 newElm->setFlippedY(oldElm->isFlippedY());
829 if (newElm->hasAudio() && oldElm->hasAudio()) {
830 newElm->setAudio(oldElm->audio());
833 if (newElm->hasVolume() && oldElm->hasVolume()) {
834 newElm->setVolume(oldElm->volume());
837 if (newElm->hasDelay() && oldElm->hasDelay()) {
838 newElm->setDelay(oldElm->delay());
846 transferPortConnections(oldElm, newElm,
true, deleted);
847 transferPortConnections(oldElm, newElm,
false, deleted);
851 const int oldId = oldElm->id();
855 m_scene->updateItemId(newElm, oldId);
857 newElm->updatePortsProperties();
862 bool isInput, QList<DeletedConnectionInfo> *deleted)
865 for (
int port = 0; port < portCount; ++port) {
866 Port *oldPort = isInput ?
static_cast<Port *
>(oldElm->
inputPort(port))
867 : static_cast<Port *>(oldElm->outputPort(port));
869 auto *conn = oldPort->connections().constFirst();
870 if (!conn) { break; }
871 const bool ownsSide = isInput ? (conn->endPort() == oldElm->
inputPort(port))
872 : (conn->startPort() == oldElm->
outputPort(port));
873 if (!ownsSide) {
break; }
874 Port *newPort = isInput ?
static_cast<Port *
>(newElm->
inputPort(port))
875 :
static_cast<Port *
>(newElm->
outputPort(port));
878 conn->setEndPort(
static_cast<InputPort *
>(newPort));
880 conn->setStartPort(
static_cast<OutputPort *
>(newPort));
882 conn->setHighLight(
false);
885 Port *otherPort = isInput ?
static_cast<Port *
>(conn->startPort())
886 : static_cast<Port *>(conn->endPort());
888 deleted->append({conn->id(), oldElm->id(), port, isInput, otherPort->graphicElement()->id(), otherPort->index()});
890 conn->setStartPort(
nullptr);
891 conn->setEndPort(
nullptr);
903 if (items.isEmpty()) {
907 setText(tr(
"Flip %1 elements in axis %2").arg(items.size()).arg(axis));
908 m_positions.reserve(items.size());
912 double xmin = items.constFirst()->pos().rx();
913 double ymin = items.constFirst()->pos().ry();
917 for (
auto *item : items) {
918 m_positions.append(item->pos());
919 xmin = (std::min)(xmin, item->pos().rx());
920 ymin = (std::min)(ymin, item->pos().ry());
921 xmax = (std::max)(xmax, item->pos().rx());
922 ymax = (std::max)(ymax, item->pos().ry());
925 m_minPos = QPointF(xmin, ymin);
926 m_maxPos = QPointF(xmax, ymax);
940 auto pos = elm->pos();
945 (m_axis == 0) ? pos.setX(m_minPos.rx() + (m_maxPos.rx() - pos.rx()))
946 : pos.setY(m_minPos.ry() + (m_maxPos.ry() - pos.ry()));
954 (m_axis == 0) ? elm->setFlippedX(!elm->isFlippedX())
955 : elm->setFlippedY(!elm->isFlippedY());
958 m_scene->setAutosaveRequired();
963 , m_newPortSize(newPortSize)
966 setText(isInput ? tr(
"Change input size to %1").arg(newPortSize)
967 : tr(
"Change output size to %1").arg(newPortSize));
980 QList<GraphicElement *> serializationOrder;
981 serializationOrder.reserve(
elements.size());
984 QDataStream stream(&m_oldData, QIODevice::WriteOnly);
989 serializationOrder.append(elm);
991 const int oldSize = m_isInput ? elm->inputSize() : elm->outputSize();
993 for (
int port = m_newPortSize; port < oldSize; ++port) {
994 Port *nport = m_isInput ?
static_cast<Port *
>(elm->inputPort(port)) : elm->outputPort(port);
996 Port *otherPort = m_isInput ?
static_cast<Port *
>(conn->startPort()) : conn->endPort();
1006 elm->setInputSize(m_newPortSize);
1008 elm->setOutputSize(m_newPortSize);
1009 elm->setSelected(
true);
1015 for (
auto *elm : serializationOrder) {
1016 m_order.append(elm->id());
1019 m_scene->setCircuitUpdateRequired();
1029 QDataStream stream(&m_oldData, QIODevice::ReadOnly);
1032 QHash<quint64, Port *> portMap;
1035 for (
auto *elm : serializationOrder) {
1036 elm->load(stream, context);
1040 int connCount; stream >> connCount;
1041 for (
int i = 0; i < connCount; ++i) {
1042 int connId; stream >> connId;
1044 conn->load(stream, context);
1045 m_scene->updateItemId(conn, connId);
1049 elm->setSelected(
true);
1052 m_scene->setCircuitUpdateRequired();
1056 : QUndoCommand(parent)
1059 m_id = element->
id();
1061 setText(tr(
"Toggle TruthTable Output at position: %1").arg(m_pos));
1070 if (!truthtable)
throw PANDACEPTION(
"Could not find truthtable element!");
1076 if (m_pos < 0 || m_pos >= truthtable->key().size()) {
1077 throw PANDACEPTION(
"TruthTable toggle position out of range: %1", QString::number(m_pos));
1080 truthtable->key().toggleBit(m_pos);
1082 m_scene->setCircuitUpdateRequired();
1083 emit m_scene->truthTableElementChanged(truthtable);
1096 QList<ConnectionInfo> connections;
1097 for (
auto *target : targets) {
1098 for (
int i = 0; i < target->inputSize(); ++i) {
1099 for (
auto *conn : target->inputPort(i)->connections()) {
1100 auto *otherPort = conn->startPort();
1102 connections.append({conn->id(), target->id(), i,
true,
1107 for (
int i = 0; i < target->outputSize(); ++i) {
1108 for (
auto *conn : target->outputPort(i)->connections()) {
1109 auto *otherPort = conn->endPort();
1111 connections.append({conn->id(), target->id(), i,
false,
1123 : QUndoCommand(parent)
1124 , m_blobName(blobName)
1128 setText(tr(
"Register blob \"%1\"").arg(blobName));
1133 m_scene->icRegistry()->registerBlob(m_blobName, m_data);
1138 m_scene->icRegistry()->removeBlob(m_blobName);
1144 : QUndoCommand(parent)
1145 , m_blobName(blobName)
1148 , m_data(scene->icRegistry()->blob(blobName))
1151 setText(tr(
"Remove blob \"%1\"").arg(blobName));
1156 m_scene->icRegistry()->removeBlob(m_blobName);
1161 m_scene->icRegistry()->setBlob(m_blobName, m_data);
1167 : QUndoCommand(parent)
1168 , m_oldName(oldName)
1169 , m_newName(newName)
1172 setText(tr(
"Rename IC \"%1\" to \"%2\"").arg(oldName, newName));
1177 m_scene->icRegistry()->renameBlob(m_oldName, m_newName);
1182 m_scene->icRegistry()->renameBlob(m_newName, m_oldName);
1188 const QList<ConnectionInfo> &connections,
Scene *scene, QUndoCommand *parent)
1190 , m_oldData(oldData)
1191 , m_connections(connections)
1193 QDataStream stream(&m_newData, QIODevice::WriteOnly);
1201 m_blobName =
elements.first()->blobName();
1202 m_newBlob =
m_scene->icRegistry()->blob(m_blobName);
1205 setText(tr(
"Update %1 IC blobs").arg(
elements.size()));
1212 auto *reg =
m_scene->icRegistry();
1214 if (!m_blobName.isEmpty()) {
1215 if (m_newBlob.isEmpty()) {
1216 reg->removeBlob(m_blobName);
1218 reg->setBlob(m_blobName, m_newBlob);
1221 loadData(m_newData);
1222 reconnectConnections();
1223 m_scene->setCircuitUpdateRequired();
1230 auto *reg =
m_scene->icRegistry();
1232 if (!m_blobName.isEmpty()) {
1233 if (m_oldBlob.isEmpty()) {
1234 reg->removeBlob(m_blobName);
1236 reg->setBlob(m_blobName, m_oldBlob);
1239 loadData(m_oldData);
1240 reconnectConnections();
1241 m_scene->setCircuitUpdateRequired();
1244void UpdateBlobCommand::loadData(QByteArray &itemData)
1247 if (elements.isEmpty()) {
1251 QDataStream stream(&itemData, QIODevice::ReadOnly);
1254 QHash<quint64, Port *> portMap;
1258 elm->load(stream, context);
1259 elm->setSelected(
true);
1263void UpdateBlobCommand::reconnectConnections()
1265 for (
const auto &ci : std::as_const(m_connections)) {
1266 auto *elm =
dynamic_cast<GraphicElement *
>(
m_scene->itemById(ci.elementId));
1267 auto *otherElm =
dynamic_cast<GraphicElement *
>(
m_scene->itemById(ci.otherElementId));
1268 if (!elm || !otherElm) {
1272 InputPort *inPort =
nullptr;
1273 OutputPort *outPort =
nullptr;
1276 inPort = (ci.portIndex >= 0 && ci.portIndex < elm->inputSize()) ? elm->inputPort(ci.portIndex) :
nullptr;
1277 outPort = (ci.otherPortIndex >= 0 && ci.otherPortIndex < otherElm->outputSize()) ? otherElm->outputPort(ci.otherPortIndex) :
nullptr;
1279 outPort = (ci.portIndex >= 0 && ci.portIndex < elm->outputSize()) ? elm->outputPort(ci.portIndex) :
nullptr;
1280 inPort = (ci.otherPortIndex >= 0 && ci.otherPortIndex < otherElm->inputSize()) ? otherElm->inputPort(ci.otherPortIndex) :
nullptr;
1283 if (!inPort || !outPort) {
1293 bool alreadyConnected =
false;
1295 if (conn->startPort() == outPort) {
1296 alreadyConnected =
true;
1300 if (alreadyConnected) {
1304 auto *conn =
new Connection();
1305 conn->setStartPort(outPort);
1306 conn->setEndPort(inPort);
1307 m_scene->updateItemId(conn, ci.connectionId);
All QUndoCommand subclasses and the CommandUtils helper namespace.
Common logging utilities, the Pandaception error type, and helper macros.
#define PANDACEPTION(msg,...)
#define PANDACEPTION_WITH_CONTEXT(context, msg,...)
#define qCDebug(category)
Connection: a wire that connects an output port to an input port in the circuit scene.
Shared numeric constants used across layers.
Singleton factory for all circuit element types.
Enums::ElementType ElementType
Abstract base class for all graphical circuit elements.
IC definition registry with file watching and embedded blob storage.
Port classes: Port (base), InputPort, and OutputPort.
Main circuit editing scene with undo/redo and user interaction.
Deserialization/serialization context structs passed through load()/save() call chains.
Circuit and waveform file serialization/deserialization utilities.
RAII guard that temporarily stops the simulation while in scope.
Synchronous cycle-based simulation engine with event-driven clock support.
Graphic element for a user-programmable truth table.
void undo() override
Removes the items from the scene.
void redo() override
Adds the items to the scene.
AddItemsCommand(const QList< QGraphicsItem * > &items, Scene *scene, QUndoCommand *parent=nullptr)
Constructs the command for adding items.
void undo() override
Restores the old port count and connections.
void redo() override
Applies the new port count.
ChangePortSizeCommand(const QList< GraphicElement * > &elements, const int newPortSize, Scene *scene, const bool isInput, QUndoCommand *parent=nullptr)
Constructs the command.
A bezier-curve wire connecting an output port to an input port in the scene.
OutputPort * startPort() const
Returns the output port this connection originates from.
InputPort * endPort() const
Returns the input port this connection leads to.
double angle()
Returns the current angle of the bezier midpoint in radians.
DeleteItemsCommand(const QList< QGraphicsItem * > &items, Scene *scene, QUndoCommand *parent=nullptr)
Constructs the command for deleting items.
void undo() override
Restores the items to the scene.
void redo() override
Removes the items from the scene.
static GraphicElement * buildElement(const ElementType type)
Constructs and returns a new graphic element of the given type.
ElementsCommand(const QList< GraphicElement * > &elements, Scene *scene, QUndoCommand *parent=nullptr)
QList< GraphicElement * > elements() const
Returns the live element pointers for this command's targets, looked up by stored ID.
FlipCommand(const QList< GraphicElement * > &items, const int axis, Scene *scene, QUndoCommand *parent=nullptr)
Constructs the flip command.
void redo() override
Applies the flip transformation.
void undo() override
Reverses the flip transformation (involution: flip twice = identity).
Abstract base class for all graphical circuit elements in wiRedPanda.
int inputSize() const
Returns the current number of input ports.
InputPort * inputPort(const int index=0) const
Returns the input port at index (default 0).
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).
virtual void save(QDataStream &stream, SerializationOptions options) const
Base class providing a unique integer identifier for circuit items.
int id() const
Returns the unique integer identifier of this item, or -1 if unassigned.
MorphCommand(const QList< GraphicElement * > &elements, ElementType type, Scene *scene, QUndoCommand *parent=nullptr)
Constructs the morph command.
void undo() override
Restores the original element types.
void redo() override
Replaces elements with instances of the new type.
MoveCommand(const QList< GraphicElement * > &list, const QList< QPointF > &oldPositions, Scene *scene, QUndoCommand *parent=nullptr)
Constructs the command capturing old and new positions.
void undo() override
Restores elements to their old positions.
void redo() override
Moves elements to their new positions.
Abstract base class for circuit element ports (connection endpoints).
GraphicElement * graphicElement()
Returns the graphic element that owns this port.
int index() const
Returns the port's visual/logical index within the element.
const QList< Connection * > & connections() const
Returns the list of wires attached to this port.
RegisterBlobCommand(const QString &blobName, const QByteArray &data, Scene *scene, QUndoCommand *parent=nullptr)
RemoveBlobCommand(const QString &blobName, Scene *scene, QUndoCommand *parent=nullptr)
RenameBlobCommand(const QString &oldName, const QString &newName, Scene *scene, QUndoCommand *parent=nullptr)
void undo() override
Reverses the rotation.
void redo() override
Applies the rotation.
RotateCommand(const QList< GraphicElement * > &items, const int angle, Scene *scene, QUndoCommand *parent=nullptr)
Constructs the command.
Main circuit editing scene.
void removeItem(QGraphicsItem *item)
Removes item from the scene and unregisters it from the per-scene ID registry.
ItemWithId * itemById(int id) const
Returns the item registered under id, or nullptr if not found.
void addItem(QGraphicsItem *item)
Adds item to the scene and registers it in the per-scene ID registry.
void updateItemId(ItemWithId *item, int newId)
Reassigns the ID of item to newId without adding it to the scene.
SerializationContext deserializationContext(QHash< quint64, Port * > &portMap, const QVersionNumber &version, SerializationPurpose purpose)
static QList< QGraphicsItem * > deserialize(QDataStream &stream, SerializationContext &context)
Deserializes items from stream until the stream is exhausted.
static void serialize(const QList< QGraphicsItem * > &items, QDataStream &stream, SerializationOptions options)
Serializes items to stream in the current .panda binary format.
static void writePandaHeader(QDataStream &stream)
Writes the .panda circuit file header to stream.
static QVersionNumber readPandaHeader(QDataStream &stream)
Reads and validates the .panda circuit file header; returns the stored version number.
RAII guard that stops the simulation on construction and restarts it on destruction.
SplitCommand(Connection *conn, QPointF mousePos, Scene *scene, QUndoCommand *parent=nullptr)
Constructs the split command.
void undo() override
Removes the node and restores the original connection.
void redo() override
Inserts the node and splits the connection.
void undo() override
Toggles the bit again (involution).
void redo() override
Toggles the bit at pos.
ToggleTruthTableOutputCommand(GraphicElement *element, int pos, Scene *scene, QUndoCommand *parent=nullptr)
Constructs the toggle command.
UpdateBlobCommand(const QList< GraphicElement * > &elements, const QByteArray &oldData, const QList< ConnectionInfo > &connections, Scene *scene, QUndoCommand *parent=nullptr)
static QList< ConnectionInfo > captureConnections(const QList< GraphicElement * > &targets)
Captures connection topology for all target elements before a blob operation.
UpdateCommand(const QList< GraphicElement * > &elements, const QByteArray &oldData, Scene *scene, QUndoCommand *parent=nullptr)
Constructs the command.
void undo() override
Restores the old property values.
void redo() override
Applies the new property values.
const QList< GraphicElement * > findElements(Scene *scene, const QList< int > &ids)
void addItems(Scene *scene, const QList< QGraphicsItem * > &items)
void saveItems(Scene *scene, QByteArray &itemData, const QList< QGraphicsItem * > &items, const QList< int > &otherIds)
GraphicElement * findElm(Scene *scene, const int id)
void storeIds(const QList< QGraphicsItem * > &items, QList< int > &ids)
const QList< QGraphicsItem * > findItems(Scene *scene, const QList< int > &ids)
Connection * findConn(Scene *scene, const int id)
const QList< QGraphicsItem * > loadItems(Scene *scene, QByteArray &itemData, const QList< int > &ids, QList< int > &otherIds)
void drainPortConnections(GraphicElement *elm, int fromPort, int toPort, bool isInput, QDataStream &stream, Scene *scene)
Saves and deletes connections on ports in range [fromPort, toPort).
const QList< QGraphicsItem * > loadList(const QList< QGraphicsItem * > &items, QList< int > &ids, QList< int > &otherIds)
void storeOtherIds(const QList< QGraphicsItem * > &connections, const QList< int > &ids, QList< int > &otherIds)
void deleteItems(Scene *scene, const QList< QGraphicsItem * > &items)
constexpr int gridSize
Scene grid unit in pixels (elements snap to gridSize/2).