Improve circuit plotting.

This commit is contained in:
jmsgrogan 2023-01-24 12:59:00 +00:00
parent 6274c41a80
commit df450a7be0
17 changed files with 321 additions and 13 deletions

View file

@ -9,6 +9,7 @@ list(APPEND HEADERS
gates/LogicGate.h
gates/BasicLogicGates.h
visuals/ElectronicCircuitNode.h
visuals/CircuitElementNode.h
visuals/WireNode.h
visuals/TerminalNode.h
visuals/LogicGateNode.h
@ -24,6 +25,7 @@ list(APPEND SOURCES
gates/BasicLogicGates.cpp
gates/LogicGate.cpp
visuals/ElectronicCircuitNode.cpp
visuals/CircuitElementNode.cpp
visuals/WireNode.cpp
visuals/TerminalNode.cpp
visuals/LogicGateNode.cpp

View file

@ -25,11 +25,21 @@ public:
return mInputTerminals;
}
const std::vector<Terminal*>& getOutputTerminals() const
{
return mOutputTerminals;
}
const std::vector<LogicGate*>& getLogicGates() const
{
return mLogicGates;
}
const std::vector<Wire*>& getWires() const
{
return mWires;
}
private:
std::vector<Terminal*> mInputTerminals;
std::vector<Terminal*> mOutputTerminals;

View file

@ -29,7 +29,7 @@ std::size_t NInMOutLogicGate::getNumInputs() const
return mNumIn;
}
std::size_t NInMOutLogicGate::getNumOutputputs() const
std::size_t NInMOutLogicGate::getNumOutputs() const
{
return mNumOut;
}

View file

@ -22,7 +22,7 @@ public:
virtual std::size_t getNumInputs() const = 0;
virtual std::size_t getNumOutputputs() const = 0;
virtual std::size_t getNumOutputs() const = 0;
virtual Wire* getInput(std::size_t idx) const = 0;
@ -47,7 +47,7 @@ public:
std::size_t getNumInputs() const override;
std::size_t getNumOutputputs() const override;
std::size_t getNumOutputs() const override;
Wire* getInput(std::size_t idx) const override;

View file

@ -0,0 +1,12 @@
#include "CircuitElementNode.h"
CircuitElementNode::CircuitElementNode(const Transform& transform)
: AbstractVisualNode(transform)
{
}
CircuitElementNode::~CircuitElementNode()
{
}

View file

@ -0,0 +1,15 @@
#pragma once
#include "AbstractVisualNode.h"
class Wire;
class CircuitElementNode : public AbstractVisualNode
{
public:
CircuitElementNode(const Transform& transform);
virtual ~CircuitElementNode();
virtual Point getConnectionLocation(Wire* wire) const = 0;
};

View file

@ -4,6 +4,8 @@
#include "WireNode.h"
#include "LogicGateNode.h"
#include "FileLogger.h"
ElectronicCircuitNode::ElectronicCircuitNode(const Transform& transform)
: AbstractVisualNode(transform)
{
@ -21,17 +23,48 @@ void ElectronicCircuitNode::setContent(ElectronicCircuit* content)
mContentDirty = true;
}
void ElectronicCircuitNode::buildWireConnections()
{
mWireInputConnections.clear();
mWireOutputConnections.clear();
for (auto terminal : mContent->getInputTerminals())
{
mWireOutputConnections[terminal->getConnection()] = terminal;
}
for (auto gate : mContent->getLogicGates())
{
for (std::size_t idx = 0; idx < gate->getNumInputs(); idx++)
{
mWireInputConnections[gate->getInput(idx)] = gate;
}
for (std::size_t idx = 0; idx < gate->getNumOutputs(); idx++)
{
mWireOutputConnections[gate->getOutput(idx)] = gate;
}
}
for (auto terminal : mContent->getOutputTerminals())
{
mWireInputConnections[terminal->getConnection()] = terminal;
}
}
void ElectronicCircuitNode::createOrUpdateGeometry(SceneInfo*)
{
mInputTerminalNodes.clear();
mWireNodes.clear();
mLogicGateNodes.clear();
buildWireConnections();
// Layout terminals
double terminal_vertical_spacing = 100;
double terminal_left_margin = 10;
double terminal_y = 0;
double terminal_y = 10;
for (auto terminal : mContent->getInputTerminals())
{
Point loc{ terminal_left_margin, terminal_y };
@ -40,6 +73,8 @@ void ElectronicCircuitNode::createOrUpdateGeometry(SceneInfo*)
terminal_node->setContent(terminal);
addChild(terminal_node.get());
mNodesForContent[terminal] = terminal_node.get();
mInputTerminalNodes.push_back(std::move(terminal_node));
terminal_y += terminal_vertical_spacing;
@ -59,12 +94,48 @@ void ElectronicCircuitNode::createOrUpdateGeometry(SceneInfo*)
gate_node->setContent(gate);
addChild(gate_node.get());
mNodesForContent[gate] = gate_node.get();
mLogicGateNodes.push_back(std::move(gate_node));
gate_x += logic_gate_vertical_spacing;
gate_y += logic_gate_horizontal_spacing;
}
// Layout output terminals
terminal_y = 10;
double terminal_right_margin = 10;
unsigned width = 500;
for (auto terminal : mContent->getOutputTerminals())
{
Point loc{ width - terminal_right_margin, terminal_y };
auto node = std::make_unique<TerminalNode>(Transform(loc));
node->setContent(terminal);
addChild(node.get());
mNodesForContent[terminal] = node.get();
mOutputTerminalNodes.push_back(std::move(node));
terminal_y += terminal_vertical_spacing;
}
// Add wires
for (auto wire : mContent->getWires())
{
auto start_node = mNodesForContent[mWireOutputConnections[wire]];
auto end_node = mNodesForContent[mWireInputConnections[wire]];
auto wire_node = std::make_unique<WireNode>(Transform());
wire_node->setInputLocation(start_node->getConnectionLocation(wire));
wire_node->setOutputLocation(end_node->getConnectionLocation(wire));
addChild(wire_node.get());
mWireNodes.push_back(std::move(wire_node));
}
}
void ElectronicCircuitNode::update(SceneInfo* sceneInfo)

View file

@ -4,9 +4,13 @@
#include "ElectronicCircuit.h"
#include <unordered_map>
class WireNode;
class TerminalNode;
class LogicGateNode;
class Wire;
class CircuitElementNode;
class ElectronicCircuitNode : public AbstractVisualNode
{
@ -22,10 +26,18 @@ public:
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo);
void buildWireConnections();
ElectronicCircuit* mContent{ nullptr };
bool mContentDirty{ true };
std::vector<std::unique_ptr<TerminalNode> > mInputTerminalNodes;
std::vector<std::unique_ptr<TerminalNode> > mOutputTerminalNodes;
std::vector<std::unique_ptr<WireNode> > mWireNodes;
std::vector<std::unique_ptr<LogicGateNode> > mLogicGateNodes;
std::unordered_map<Wire*, CircuitElement*> mWireInputConnections;
std::unordered_map<Wire*, CircuitElement*> mWireOutputConnections;
std::unordered_map<CircuitElement*, CircuitElementNode*> mNodesForContent;
};

View file

@ -8,7 +8,7 @@
#include "LogicGatePrimitiveShapes.h"
LogicGateNode::LogicGateNode(const Transform& transform)
: AbstractVisualNode(transform)
: CircuitElementNode(transform)
{
}
@ -18,6 +18,45 @@ LogicGateNode::~LogicGateNode()
}
Point LogicGateNode::getConnectionLocation(Wire* wire) const
{
bool is_input{ false };
std::size_t connection_id{ 0 };
for (std::size_t idx = 0; idx < mContent->getNumInputs(); idx++)
{
if (mContent->getInput(idx) == wire)
{
is_input = true;
connection_id = idx;
break;
}
}
for (std::size_t idx = 0; idx < mContent->getNumOutputs(); idx++)
{
if (mContent->getOutput(idx) == wire)
{
connection_id = idx;
break;
}
}
Point loc;
if (mContent->getGateType() == LogicGate::GateType::AND)
{
loc = LogicGatePrimitiveShapes::getAndGateConnectionLocation(is_input, connection_id);
}
else if (mContent->getGateType() == LogicGate::GateType::OR)
{
loc = LogicGatePrimitiveShapes::getOrGateConnectionLocation(is_input, connection_id);
}
loc.move(mTransform.getLocation().getX(), mTransform.getLocation().getY());
return loc;
}
void LogicGateNode::setContent(LogicGate* content)
{
mContent = content;

View file

@ -1,19 +1,21 @@
#pragma once
#include "AbstractVisualNode.h"
#include "CircuitElementNode.h"
#include "CircleNode.h"
class LogicGate;
class PathNode;
class LogicGateNode : public AbstractVisualNode
class LogicGateNode : public CircuitElementNode
{
public:
LogicGateNode(const Transform& transform);
virtual ~LogicGateNode();
Point getConnectionLocation(Wire* wire) const override;
void setContent(LogicGate* content);
void update(SceneInfo* sceneInfo);

View file

@ -5,7 +5,45 @@ std::string LogicGatePrimitiveShapes::getAndGateShape()
return "M4 8 h24 a16 16 0 0 1 0 32 h-24Z";
}
Point LogicGatePrimitiveShapes::getAndGateConnectionLocation(bool isInput, std::size_t idx)
{
if (isInput)
{
if (idx == 0)
{
return { 4.0, 18.66 };
}
else
{
return { 4.0, 29.33 };
}
}
else
{
return {44.0, 24.0};
}
}
std::string LogicGatePrimitiveShapes::getOrGateShape()
{
return "M4 8 h16 q16 2 24 16 q-12 16 -24 16 h-16 q12 -16 0 -32Z";
}
Point LogicGatePrimitiveShapes::getOrGateConnectionLocation(bool isInput, std::size_t idx)
{
if (isInput)
{
if (idx == 0)
{
return { 8.0, 18.66 };
}
else
{
return { 8.0, 29.33 };
}
}
else
{
return { 44.0, 24.0 };
}
}

View file

@ -1,11 +1,17 @@
#pragma once
#include "Point.h"
#include <string>
class LogicGatePrimitiveShapes
{
public:
static Point getAndGateConnectionLocation(bool isInput, std::size_t idx);
static std::string getAndGateShape();
static Point getOrGateConnectionLocation(bool isInput, std::size_t idx);
static std::string getOrGateShape();
};

View file

@ -3,7 +3,7 @@
#include "CircleNode.h"
TerminalNode::TerminalNode(const Transform& transform)
: AbstractVisualNode(transform)
: CircuitElementNode(transform)
{
}
@ -17,7 +17,7 @@ void TerminalNode::createOrUpdateGeometry(SceneInfo*)
{
if (!mMarker)
{
mMarker = std::make_unique<CircleNode>(Transform{}, 5);
mMarker = std::make_unique<CircleNode>(Transform{}, 3);
mMarker->setFillColor(Color(0, 0, 0));
mMarker->setHasStrokeColor(false);
@ -33,3 +33,8 @@ void TerminalNode::update(SceneInfo* sceneInfo)
mContentDirty = false;
}
}
Point TerminalNode::getConnectionLocation(Wire*) const
{
return mTransform.getLocation();
}

View file

@ -1,16 +1,18 @@
#pragma once
#include "AbstractVisualNode.h"
#include "CircuitElementNode.h"
#include "Terminal.h"
class CircleNode;
class TerminalNode : public AbstractVisualNode
class TerminalNode : public CircuitElementNode
{
public:
TerminalNode(const Transform& transform);
Point getConnectionLocation(Wire* wire) const override;
void setContent(Terminal* terminal);
void update(SceneInfo* sceneInfo) override;

View file

@ -0,0 +1,69 @@
#include "WireNode.h"
#include "LineNode.h"
WireNode::WireNode(const Transform& transform)
: AbstractVisualNode(transform)
{
}
void WireNode::setContent(Wire* wire)
{
mContent = wire;
mContentDirty = true;
}
void WireNode::setInputLocation(const Point& point)
{
if (mInputLocation != point)
{
mContentDirty = true;
mInputLocation = point;
}
}
void WireNode::setOutputLocation(const Point& point)
{
if (mOutputLocation != point)
{
mContentDirty = true;
mOutputLocation = point;
}
}
void WireNode::update(SceneInfo* sceneInfo)
{
if (mContentDirty)
{
createOrUpdateGeometry(sceneInfo);
mContentDirty = false;
}
}
void WireNode::createOrUpdateGeometry(SceneInfo*)
{
if (!mLine)
{
auto loc = mOutputLocation;
loc.move(-mInputLocation.getX(), -mInputLocation.getY(), -mInputLocation.getZ());
std::vector<Point> points;
if (loc.getY() == 0.0)
{
points = { loc };
}
else
{
auto join0 = Point(loc.getX() * 3.0 / 4.0, 0.0);
auto join1 = Point(loc.getX() * 3.0 / 4.0, loc.getY());
points = { join0, join1 , loc};
}
mLine = std::make_unique<LineNode>(Transform(mInputLocation), points);
addChild(mLine.get());
}
}

View file

@ -1,6 +1,30 @@
#pragma once
class WireNode
{
#include "AbstractVisualNode.h"
#include "Wire.h"
class LineNode;
class WireNode : public AbstractVisualNode
{
public:
WireNode(const Transform& transform);
void setContent(Wire* wire);
void setInputLocation(const Point& point);
void setOutputLocation(const Point& point);
void update(SceneInfo* sceneInfo);
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo);
Wire* mContent{ nullptr };
bool mContentDirty{ true };
Point mInputLocation;
Point mOutputLocation;
std::unique_ptr<LineNode> mLine;
};

View file

@ -52,6 +52,7 @@ TEST_CASE(TestElectronicCircuit, "circuits")
circuit->addWire(std::move(wire1));
circuit->addWire(std::move(wire2));
circuit->addWire(std::move(wire3));
circuit->addWire(std::move(wire4));
circuit->addInputTerminal(std::move(input0));
circuit->addInputTerminal(std::move(input1));