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()) {
206 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()));
210 for (
int i = 0; i < items.size(); ++i) {
211 if (
auto *itemId =
dynamic_cast<ItemWithId *
>(items.at(i))) {
223 for (
auto it = items.rbegin(); it != items.rend(); ++it) {
230 bool isInput, QDataStream &stream,
Scene *scene)
233 for (
int port = fromPort; port < toPort; ++port) {
235 connCount +=
static_cast<int>(p->
connections().size());
239 for (
int port = fromPort; port < toPort; ++port) {
243 stream << conn->id();
245 conn->setStartPort(
nullptr);
246 conn->setEndPort(
nullptr);
256 : QUndoCommand(parent)
261 m_ids.append(elm->id());
271 : QUndoCommand(parent)
283 setText(tr(
"Add %1 elements").arg(items_.size()));
293 m_scene->setCircuitUpdateRequired();
301 m_scene->setCircuitUpdateRequired();
305 : QUndoCommand(parent)
309 setText(tr(
"Delete %1 elements").arg(items_.size()));
317 m_scene->setCircuitUpdateRequired();
327 m_scene->setCircuitUpdateRequired();
334 setText(tr(
"Rotate %1 degrees").arg(m_angle));
335 m_positions.reserve(items.size());
337 for (
auto *item : items) {
338 m_positions.append(item->pos());
339 item->setPos(item->pos());
348 for (
int i = 0; i <
elements.size(); ++i) {
350 elm->setRotation(elm->rotation() - m_angle);
351 elm->setPos(m_positions.at(i));
353 elm->setSelected(
true);
356 m_scene->setAutosaveRequired();
367 cx += elm->pos().x();
368 cy += elm->pos().y();
379 QTransform transform;
380 transform.translate(cx, cy);
381 transform.rotate(m_angle);
382 transform.translate(-cx, -cy);
385 elm->setPos(transform.map(elm->pos()));
386 elm->setRotation(elm->rotation() + m_angle);
389 m_scene->setAutosaveRequired();
394 , m_oldPositions(oldPositions)
396 m_newPositions.reserve(list.size());
398 for (
auto *elm : list) {
399 m_newPositions.append(elm->pos());
402 setText(tr(
"Move elements"));
409 for (
int i = 0; i <
elements.size(); ++i) {
410 elements.at(i)->setPos(m_oldPositions.at(i));
413 m_scene->setAutosaveRequired();
420 for (
int i = 0; i <
elements.size(); ++i) {
421 elements.at(i)->setPos(m_newPositions.at(i));
424 m_scene->setAutosaveRequired();
434 QDataStream stream(&m_newData, QIODevice::WriteOnly);
449 const auto newState = snapshotWirelessState();
450 if (!newState.isEmpty()) {
453 m_wirelessTopologyChange = wirelessStateDiffers(snapshotWirelessState(), newState);
457 setText(tr(
"Update %1 elements").arg(
elements.size()));
468 refreshRuntimeState();
476 refreshRuntimeState();
479QVector<UpdateCommand::WirelessState> UpdateCommand::snapshotWirelessState()
const
481 QVector<WirelessState> state;
484 if (elm && elm->hasWirelessMode()) {
485 state.append({elm, elm->wirelessMode(), elm->label()});
491bool UpdateCommand::wirelessStateDiffers(
const QVector<WirelessState> &before,
492 const QVector<WirelessState> &after)
498 if (before.size() != after.size()) {
501 for (
int i = 0; i < before.size(); ++i) {
502 const auto &b = before.at(i);
503 const auto &a = after.at(i);
504 if (b.element != a.element || b.mode != a.mode) {
507 if (b.label != a.label && a.mode != WirelessMode::None) {
514void UpdateCommand::refreshRuntimeState()
519 if (m_wirelessTopologyChange) {
520 m_scene->setCircuitUpdateRequired();
528 m_scene->setPropertyUpdateRequired();
531void UpdateCommand::loadData(QByteArray &itemData)
539 QDataStream stream(&itemData, QIODevice::ReadOnly);
542 QHash<quint64, Port *> portMap;
546 elm->load(stream, context);
547 elm->setSelected(
true);
552 : QUndoCommand(parent)
560 m_nodePos = mousePos - node->pixmapCenter();
562 qreal xV = qRound(m_nodePos.x() / gridSize) * gridSize;
563 qreal yV = qRound(m_nodePos.y() / gridSize) * gridSize;
564 m_nodePos = QPointF(xV, yV);
571 const int angle =
static_cast<int>(conn->
angle());
572 m_nodeAngle =
static_cast<int>(360 - 90 * (std::round(angle / 90.0)));
576 auto *endPort = conn->
endPort();
577 if (!startPort || !endPort) {
578 throw PANDACEPTION(
"Invalid connection ports in SplitCommand constructor");
582 auto *endElement = endPort->graphicElement();
583 if (!startElement || !endElement) {
584 throw PANDACEPTION(
"Invalid graphic elements in SplitCommand constructor");
587 m_elm1Id = startElement->
id();
588 m_elm2Id = endElement->id();
596 m_scene->addItem(conn2);
597 m_c2Id = conn2->id();
598 m_scene->removeItem(conn2);
602 m_scene->addItem(node);
603 m_nodeId = node->id();
604 m_scene->removeItem(node);
607 setText(tr(
"Wire split"));
622 if (!conn1 || !elm1 || !elm2) {
633 m_scene->updateItemId(conn2, m_c2Id);
638 m_scene->updateItemId(node, m_nodeId);
641 node->setPos(m_nodePos);
642 node->setRotation(m_nodeAngle);
644 auto *endPort = conn1->endPort();
646 throw PANDACEPTION(
"Error: endPort is null in SplitCommand::redo()");
650 conn2->setStartPort(node->outputPort());
651 conn2->setEndPort(endPort);
652 conn1->setEndPort(node->inputPort());
654 m_scene->addItem(node);
655 m_scene->addItem(conn2);
657 conn1->updatePosFromPorts();
658 conn2->updatePosFromPorts();
660 m_scene->setCircuitUpdateRequired();
674 if (!conn1 || !conn2 || !elm1 || !elm2 || !node) {
679 conn1->setEndPort(conn2->endPort());
681 conn1->updatePosFromPorts();
684 m_scene->removeItem(conn2);
685 m_scene->removeItem(node);
690 m_scene->setCircuitUpdateRequired();
702 m_types.append(oldElm->elementType());
705 setText(tr(
"Morph %1 elements to %2").arg(
elements.size()).arg(
elements.constFirst()->objectName()));
708void MorphCommand::restoreDeletedConnections(
const QList<DeletedConnectionInfo> &deleted)
712 for (
const auto &info : deleted) {
715 if (!morphedElm || !otherElm) {
719 auto *conn =
new Connection();
721 conn->setStartPort(otherElm->outputPort(info.otherPortIndex));
722 conn->setEndPort(morphedElm->inputPort(info.portIndex));
724 conn->setStartPort(morphedElm->outputPort(info.portIndex));
725 conn->setEndPort(otherElm->inputPort(info.otherPortIndex));
729 m_scene->updateItemId(conn, info.connectionId);
741 decltype(newElms) oldElms;
742 oldElms.reserve(
m_ids.size());
744 for (
int i = 0; i <
m_ids.size(); ++i) {
748 m_deletedConnectionsOnUndo.clear();
749 transferConnections(newElms, oldElms, &m_deletedConnectionsOnUndo);
752 restoreDeletedConnections(m_deletedConnections);
754 m_scene->setCircuitUpdateRequired();
762 decltype(oldElms) newElms;
763 newElms.reserve(
m_ids.size());
765 for (
int i = 0; i <
m_ids.size(); ++i) {
769 m_deletedConnections.clear();
770 transferConnections(oldElms, newElms, &m_deletedConnections);
775 restoreDeletedConnections(m_deletedConnectionsOnUndo);
777 m_scene->setCircuitUpdateRequired();
780void MorphCommand::transferConnections(
const QList<GraphicElement *> &from,
const QList<GraphicElement *> &to,
781 QList<DeletedConnectionInfo> *deleted)
783 for (
int elm = 0; elm < from.size(); ++elm) {
784 auto *oldElm = from.at(elm);
785 auto *newElm = to.at(elm);
787 newElm->setInputSize(oldElm->inputSize());
788 newElm->setPos(oldElm->pos());
792 if ((oldElm->elementType() == ElementType::Not) && (newElm->elementType() == ElementType::Node)) {
793 newElm->moveBy(16, 16);
796 if ((oldElm->elementType() == ElementType::Node) && (newElm->elementType() == ElementType::Not)) {
797 newElm->moveBy(-16, -16);
802 if (newElm->rotatesGraphic() && oldElm->rotatesGraphic()) {
803 newElm->setRotation(oldElm->rotation());
806 if (newElm->hasLabel() && oldElm->hasLabel()) {
807 newElm->setLabel(oldElm->label());
810 if (newElm->hasColors() && oldElm->hasColors()) {
811 newElm->setColor(oldElm->color());
814 if (newElm->hasFrequency() && oldElm->hasFrequency()) {
815 newElm->setFrequency(oldElm->frequency());
818 if (newElm->hasTrigger() && oldElm->hasTrigger()) {
819 newElm->setTrigger(oldElm->trigger());
824 newElm->setFlippedX(oldElm->isFlippedX());
825 newElm->setFlippedY(oldElm->isFlippedY());
827 if (newElm->hasAudio() && oldElm->hasAudio()) {
828 newElm->setAudio(oldElm->audio());
831 if (newElm->hasVolume() && oldElm->hasVolume()) {
832 newElm->setVolume(oldElm->volume());
835 if (newElm->hasDelay() && oldElm->hasDelay()) {
836 newElm->setDelay(oldElm->delay());
844 transferPortConnections(oldElm, newElm,
true, deleted);
845 transferPortConnections(oldElm, newElm,
false, deleted);
849 const int oldId = oldElm->id();
853 m_scene->updateItemId(newElm, oldId);
855 newElm->updatePortsProperties();
860 bool isInput, QList<DeletedConnectionInfo> *deleted)
863 for (
int port = 0; port < portCount; ++port) {
864 Port *oldPort = isInput ?
static_cast<Port *
>(oldElm->
inputPort(port))
865 : static_cast<Port *>(oldElm->outputPort(port));
867 auto *conn = oldPort->connections().constFirst();
868 if (!conn) { break; }
869 const bool ownsSide = isInput ? (conn->endPort() == oldElm->
inputPort(port))
870 : (conn->startPort() == oldElm->
outputPort(port));
871 if (!ownsSide) {
break; }
872 Port *newPort = isInput ?
static_cast<Port *
>(newElm->
inputPort(port))
873 :
static_cast<Port *
>(newElm->
outputPort(port));
876 conn->setEndPort(
static_cast<InputPort *
>(newPort));
878 conn->setStartPort(
static_cast<OutputPort *
>(newPort));
880 conn->setHighLight(
false);
883 Port *otherPort = isInput ?
static_cast<Port *
>(conn->startPort())
884 : static_cast<Port *>(conn->endPort());
886 deleted->append({conn->id(), oldElm->id(), port, isInput, otherPort->graphicElement()->id(), otherPort->index()});
888 conn->setStartPort(
nullptr);
889 conn->setEndPort(
nullptr);
901 if (items.isEmpty()) {
905 setText(tr(
"Flip %1 elements in axis %2").arg(items.size()).arg(axis));
906 m_positions.reserve(items.size());
910 double xmin = items.constFirst()->pos().rx();
911 double ymin = items.constFirst()->pos().ry();
915 for (
auto *item : items) {
916 m_positions.append(item->pos());
917 xmin = (std::min)(xmin, item->pos().rx());
918 ymin = (std::min)(ymin, item->pos().ry());
919 xmax = (std::max)(xmax, item->pos().rx());
920 ymax = (std::max)(ymax, item->pos().ry());
923 m_minPos = QPointF(xmin, ymin);
924 m_maxPos = QPointF(xmax, ymax);
938 auto pos = elm->pos();
943 (m_axis == 0) ? pos.setX(m_minPos.rx() + (m_maxPos.rx() - pos.rx()))
944 : pos.setY(m_minPos.ry() + (m_maxPos.ry() - pos.ry()));
952 (m_axis == 0) ? elm->setFlippedX(!elm->isFlippedX())
953 : elm->setFlippedY(!elm->isFlippedY());
956 m_scene->setAutosaveRequired();
961 , m_newPortSize(newPortSize)
964 setText(isInput ? tr(
"Change input size to %1").arg(newPortSize)
965 : tr(
"Change output size to %1").arg(newPortSize));
978 QList<GraphicElement *> serializationOrder;
979 serializationOrder.reserve(
elements.size());
982 QDataStream stream(&m_oldData, QIODevice::WriteOnly);
987 serializationOrder.append(elm);
989 const int oldSize = m_isInput ? elm->inputSize() : elm->outputSize();
991 for (
int port = m_newPortSize; port < oldSize; ++port) {
992 Port *nport = m_isInput ?
static_cast<Port *
>(elm->inputPort(port)) : elm->outputPort(port);
994 Port *otherPort = m_isInput ?
static_cast<Port *
>(conn->startPort()) : conn->endPort();
1004 elm->setInputSize(m_newPortSize);
1006 elm->setOutputSize(m_newPortSize);
1007 elm->setSelected(
true);
1013 for (
auto *elm : serializationOrder) {
1014 m_order.append(elm->id());
1017 m_scene->setCircuitUpdateRequired();
1027 QDataStream stream(&m_oldData, QIODevice::ReadOnly);
1030 QHash<quint64, Port *> portMap;
1033 for (
auto *elm : serializationOrder) {
1034 elm->load(stream, context);
1038 int connCount; stream >> connCount;
1039 for (
int i = 0; i < connCount; ++i) {
1040 int connId; stream >> connId;
1042 conn->load(stream, context);
1043 m_scene->updateItemId(conn, connId);
1047 elm->setSelected(
true);
1050 m_scene->setCircuitUpdateRequired();
1054 : QUndoCommand(parent)
1057 m_id = element->
id();
1059 setText(tr(
"Toggle TruthTable Output at position: %1").arg(m_pos));
1068 if (!truthtable)
throw PANDACEPTION(
"Could not find truthtable element!");
1074 if (m_pos < 0 || m_pos >= truthtable->key().size()) {
1075 throw PANDACEPTION(
"TruthTable toggle position out of range: %1", QString::number(m_pos));
1078 truthtable->key().toggleBit(m_pos);
1080 m_scene->setCircuitUpdateRequired();
1081 emit m_scene->truthTableElementChanged(truthtable);
1094 QList<ConnectionInfo> connections;
1095 for (
auto *target : targets) {
1096 for (
int i = 0; i < target->inputSize(); ++i) {
1097 for (
auto *conn : target->inputPort(i)->connections()) {
1098 auto *otherPort = conn->startPort();
1100 connections.append({conn->id(), target->id(), i,
true,
1105 for (
int i = 0; i < target->outputSize(); ++i) {
1106 for (
auto *conn : target->outputPort(i)->connections()) {
1107 auto *otherPort = conn->endPort();
1109 connections.append({conn->id(), target->id(), i,
false,
1121 : QUndoCommand(parent)
1122 , m_blobName(blobName)
1126 setText(tr(
"Register blob \"%1\"").arg(blobName));
1131 m_scene->icRegistry()->registerBlob(m_blobName, m_data);
1136 m_scene->icRegistry()->removeBlob(m_blobName);
1142 : QUndoCommand(parent)
1143 , m_blobName(blobName)
1146 , m_data(scene->icRegistry()->blob(blobName))
1149 setText(tr(
"Remove blob \"%1\"").arg(blobName));
1154 m_scene->icRegistry()->removeBlob(m_blobName);
1159 m_scene->icRegistry()->setBlob(m_blobName, m_data);
1165 : QUndoCommand(parent)
1166 , m_oldName(oldName)
1167 , m_newName(newName)
1170 setText(tr(
"Rename IC \"%1\" to \"%2\"").arg(oldName, newName));
1175 m_scene->icRegistry()->renameBlob(m_oldName, m_newName);
1180 m_scene->icRegistry()->renameBlob(m_newName, m_oldName);
1186 const QList<ConnectionInfo> &connections,
Scene *scene, QUndoCommand *parent)
1188 , m_oldData(oldData)
1189 , m_connections(connections)
1191 QDataStream stream(&m_newData, QIODevice::WriteOnly);
1199 m_blobName =
elements.first()->blobName();
1200 m_newBlob =
m_scene->icRegistry()->blob(m_blobName);
1203 setText(tr(
"Update %1 IC blobs").arg(
elements.size()));
1210 auto *reg =
m_scene->icRegistry();
1212 if (!m_blobName.isEmpty()) {
1213 if (m_newBlob.isEmpty()) {
1214 reg->removeBlob(m_blobName);
1216 reg->setBlob(m_blobName, m_newBlob);
1219 loadData(m_newData);
1220 reconnectConnections();
1221 m_scene->setCircuitUpdateRequired();
1228 auto *reg =
m_scene->icRegistry();
1230 if (!m_blobName.isEmpty()) {
1231 if (m_oldBlob.isEmpty()) {
1232 reg->removeBlob(m_blobName);
1234 reg->setBlob(m_blobName, m_oldBlob);
1237 loadData(m_oldData);
1238 reconnectConnections();
1239 m_scene->setCircuitUpdateRequired();
1242void UpdateBlobCommand::loadData(QByteArray &itemData)
1245 if (elements.isEmpty()) {
1249 QDataStream stream(&itemData, QIODevice::ReadOnly);
1252 QHash<quint64, Port *> portMap;
1256 elm->load(stream, context);
1257 elm->setSelected(
true);
1261void UpdateBlobCommand::reconnectConnections()
1263 for (
const auto &ci : std::as_const(m_connections)) {
1264 auto *elm =
dynamic_cast<GraphicElement *
>(
m_scene->itemById(ci.elementId));
1265 auto *otherElm =
dynamic_cast<GraphicElement *
>(
m_scene->itemById(ci.otherElementId));
1266 if (!elm || !otherElm) {
1270 InputPort *inPort =
nullptr;
1271 OutputPort *outPort =
nullptr;
1274 inPort = (ci.portIndex >= 0 && ci.portIndex < elm->inputSize()) ? elm->inputPort(ci.portIndex) :
nullptr;
1275 outPort = (ci.otherPortIndex >= 0 && ci.otherPortIndex < otherElm->outputSize()) ? otherElm->outputPort(ci.otherPortIndex) :
nullptr;
1277 outPort = (ci.portIndex >= 0 && ci.portIndex < elm->outputSize()) ? elm->outputPort(ci.portIndex) :
nullptr;
1278 inPort = (ci.otherPortIndex >= 0 && ci.otherPortIndex < otherElm->inputSize()) ? otherElm->inputPort(ci.otherPortIndex) :
nullptr;
1281 if (!inPort || !outPort) {
1291 bool alreadyConnected =
false;
1293 if (conn->startPort() == outPort) {
1294 alreadyConnected =
true;
1298 if (alreadyConnected) {
1302 auto *conn =
new Connection();
1303 conn->setStartPort(outPort);
1304 conn->setEndPort(inPort);
1305 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).