Initial circuits plugin work.

This commit is contained in:
jmsgrogan 2023-01-20 16:47:39 +00:00
parent b5f21900eb
commit f8a2ce3c59
50 changed files with 1451 additions and 97 deletions

View file

@ -0,0 +1,42 @@
set(PLUGIN_NAME circuits)
list(APPEND HEADERS
CircuitElement.h
Wire.h
Terminal.h
ElectronicCircuit.h
TruthTable.h
gates/LogicGate.h
gates/BasicLogicGates.h
visuals/ElectronicCircuitNode.h
visuals/WireNode.h
visuals/TerminalNode.h
visuals/LogicGateNode.h
visuals/LogicGatePrimitiveShapes.h
)
list(APPEND SOURCES
CircuitElement.cpp
Wire.cpp
Terminal.cpp
ElectronicCircuit.cpp
TruthTable.cpp
gates/BasicLogicGates.cpp
gates/LogicGate.cpp
visuals/ElectronicCircuitNode.cpp
visuals/WireNode.cpp
visuals/TerminalNode.cpp
visuals/LogicGateNode.cpp
visuals/LogicGatePrimitiveShapes.cpp
)
add_library(${PLUGIN_NAME} SHARED ${SOURCES} ${HEADERS})
target_include_directories(${PLUGIN_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/gates
${CMAKE_CURRENT_SOURCE_DIR}/visuals
)
target_link_libraries(${PLUGIN_NAME} PUBLIC core visual_elements)
set_property(TARGET ${PLUGIN_NAME} PROPERTY FOLDER plugins)
set_target_properties( ${PLUGIN_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )

View file

View file

@ -0,0 +1,28 @@
#pragma once
#include <string>
#include <memory>
class CircuitElement
{
public:
enum class Type
{
INPUT_TERMINAL,
OUTPUT_TERMINAL,
WIRE,
LOGIC_GATE,
FANOUT,
UNKNOWN
};
virtual ~CircuitElement() = default;
virtual CircuitElement::Type getType() const = 0;
};

View file

@ -0,0 +1,27 @@
#include "ElectronicCircuit.h"
#include "LogicGate.h"
void ElectronicCircuit::addInputTerminal(TerminalPtr terminal)
{
mInputTerminals.push_back(terminal.get());
mElements.push_back(std::move(terminal));
}
void ElectronicCircuit::addOutputTerminal(TerminalPtr terminal)
{
mOutputTerminals.push_back(terminal.get());
mElements.push_back(std::move(terminal));
}
void ElectronicCircuit::addWire(WirePtr wire)
{
mWires.push_back(wire.get());
mElements.push_back(std::move(wire));
}
void ElectronicCircuit::addLogicGate(LogicGatePtr gate)
{
mLogicGates.push_back(gate.get());
mElements.push_back(std::move(gate));
}

View file

@ -0,0 +1,42 @@
#pragma once
#include <memory>
#include <vector>
#include "CircuitElement.h"
#include "LogicGate.h"
#include "Terminal.h"
using LogicGatePtr = std::unique_ptr<LogicGate>;
class ElectronicCircuit
{
public:
void addInputTerminal(TerminalPtr terminal);
void addOutputTerminal(TerminalPtr terminal);
void addWire(WirePtr wire);
void addLogicGate(LogicGatePtr gate);
const std::vector<Terminal*>& getInputTerminals() const
{
return mInputTerminals;
}
const std::vector<LogicGate*>& getLogicGates() const
{
return mLogicGates;
}
private:
std::vector<Terminal*> mInputTerminals;
std::vector<Terminal*> mOutputTerminals;
std::vector<Wire*> mWires;
std::vector<LogicGate*> mLogicGates;
std::vector<std::unique_ptr<CircuitElement> > mElements;
};

View file

@ -0,0 +1,18 @@
#include "Terminal.h"
Terminal::Terminal(TerminalType type, const std::string& label)
: mType(type),
mLabel(label)
{
}
Wire* Terminal::getConnection() const
{
return mConnection;
}
void Terminal::setConnection(Wire* connection)
{
mConnection = connection;
}

View file

@ -0,0 +1,35 @@
#pragma once
#include "CircuitElement.h"
#include <string>
class Wire;
class Terminal : public CircuitElement
{
public:
enum class TerminalType
{
INPUT,
OUTPUT
};
Terminal(TerminalType type, const std::string& label = {});
Wire* getConnection() const;
Type getType() const override
{
return mType == TerminalType::INPUT ? Type::INPUT_TERMINAL : Type::OUTPUT_TERMINAL;
}
void setConnection(Wire* connection);
private:
std::string mLabel;
bool mValue{ false };
TerminalType mType;
Wire* mConnection{ nullptr };
};
using TerminalPtr = std::unique_ptr<Terminal>;

View file

@ -0,0 +1,11 @@
#include "TruthTable.h"
const TruthTable::TableData TruthTable::AND_TRUTH_TABLE = { {{0, 0}, {0}} };
TruthTable TruthTable::getAndTruthTable()
{
TruthTable table(2, 1);
table.setTable(AND_TRUTH_TABLE);
return table;
}

View file

@ -0,0 +1,32 @@
#pragma once
#include <map>
#include <vector>
class TruthTable
{
public:
using TableData = std::map<std::vector<bool>, std::vector<bool> >;
TruthTable(std::size_t numInputColumns, std::size_t numOutputColumns)
: mNumInputColumns(numInputColumns),
mNumOutputColumns(numOutputColumns)
{
}
void setTable(const TableData& data)
{
mTable = data;
}
static TruthTable getAndTruthTable();
static const TruthTable::TableData AND_TRUTH_TABLE;
private:
std::size_t mNumInputColumns{ 0 };
std::size_t mNumOutputColumns{ 0 };
TableData mTable;
};

View file

@ -0,0 +1,18 @@
#include "Wire.h"
Wire::Wire(CircuitElement* input, CircuitElement* output)
: mInput(input),
mOutput(output)
{
}
CircuitElement* Wire::getInput() const
{
return mInput;
}
CircuitElement* Wire::getOutput() const
{
return mOutput;
}

View file

@ -0,0 +1,26 @@
#pragma once
#include "CircuitElement.h"
class Wire : public CircuitElement
{
public:
Wire(CircuitElement* input, CircuitElement* output);
CircuitElement* getInput() const;
CircuitElement* getOutput() const;
Type getType() const override
{
return Type::WIRE;
}
private:
CircuitElement* mInput{ nullptr };
CircuitElement* mOutput{ nullptr };
};
using WirePtr = std::unique_ptr<Wire>;
class Fanout : public CircuitElement
{
};

View file

@ -0,0 +1,13 @@
#include "BasicLogicGates.h"
AndLogicGate::AndLogicGate(Wire* input0, Wire* input1, Wire* output)
: TwoInOneOutLogicGate(input0, input1, output)
{
}
OrLogicGate::OrLogicGate(Wire* input0, Wire* input1, Wire* output)
: TwoInOneOutLogicGate(input0, input1, output)
{
}

View file

@ -0,0 +1,45 @@
#pragma once
#include "LogicGate.h"
class AndLogicGate : public TwoInOneOutLogicGate
{
public:
AndLogicGate(Wire* input0 = nullptr, Wire* input1 = nullptr, Wire* output = nullptr);
virtual ~AndLogicGate() = default;
const TruthTable& getTruthTable()
{
return mTable;
}
GateType getGateType() const
{
return GateType::AND;
}
private:
TruthTable mTable{ TruthTable(2, 1) };
};
class OrLogicGate : public TwoInOneOutLogicGate
{
public:
OrLogicGate(Wire* input0 = nullptr, Wire* input1 = nullptr, Wire* output = nullptr);
virtual ~OrLogicGate() = default;
const TruthTable& getTruthTable()
{
return mTable;
}
GateType getGateType() const
{
return GateType::OR;
}
private:
TruthTable mTable{ TruthTable(2, 1) };
};

View file

@ -0,0 +1,96 @@
#include "LogicGate.h"
NInMOutLogicGate::NInMOutLogicGate(std::size_t numIn, std::size_t numOut, std::vector<Wire*> inputs, std::vector<Wire*> outputs)
: LogicGate(),
mNumIn(numIn),
mNumOut(numOut)
{
if (inputs.size() == mNumIn)
{
mInputs = inputs;
}
else
{
mInputs = std::vector<Wire*>(numIn, nullptr);
}
if (outputs.size() == mNumOut)
{
mOutputs = outputs;
}
else
{
mOutputs = std::vector<Wire*>(numOut, nullptr);
}
}
std::size_t NInMOutLogicGate::getNumInputs() const
{
return mNumIn;
}
std::size_t NInMOutLogicGate::getNumOutputputs() const
{
return mNumOut;
}
Wire* NInMOutLogicGate::getInput(std::size_t idx) const
{
if (idx < mNumIn)
{
return mInputs[idx];
}
else
{
return nullptr;
}
}
Wire* NInMOutLogicGate::getOutput(std::size_t idx) const
{
if (idx < mNumOut)
{
return mOutputs[idx];
}
else
{
return nullptr;
}
}
void NInMOutLogicGate::setAtInput(std::size_t idx, Wire* value)
{
if (idx < mInputs.size())
{
mInputs[idx] = value;
}
}
void NInMOutLogicGate::setAtOutput(std::size_t idx, Wire* value)
{
if (idx < mOutputs.size())
{
mOutputs[idx] = value;
}
}
TwoInOneOutLogicGate::TwoInOneOutLogicGate(Wire* input0, Wire* input1, Wire* output)
: NInMOutLogicGate(2, 1, { input0, input1 }, {output})
{
}
void TwoInOneOutLogicGate::setInput0(Wire* input)
{
setAtInput(0, input);
}
void TwoInOneOutLogicGate::setInput1(Wire* input)
{
setAtInput(1, input);
}
void TwoInOneOutLogicGate::setOutput(Wire* output)
{
setAtOutput(0, output);
}

View file

@ -0,0 +1,78 @@
#pragma once
#include "CircuitElement.h"
#include "TruthTable.h"
#include "Wire.h"
#include <memory>
#include <vector>
class LogicGate : public CircuitElement
{
public:
enum class GateType
{
NOT,
AND,
OR,
XOR,
UNKNOWN
};
virtual ~LogicGate() = default;
virtual std::size_t getNumInputs() const = 0;
virtual std::size_t getNumOutputputs() const = 0;
virtual Wire* getInput(std::size_t idx) const = 0;
virtual Wire* getOutput(std::size_t idx) const = 0;
virtual const TruthTable& getTruthTable() = 0;
virtual GateType getGateType() const = 0;
Type getType() const override
{
return Type::LOGIC_GATE;
}
};
class NInMOutLogicGate : public LogicGate
{
public:
NInMOutLogicGate(std::size_t numIn, std::size_t numOut, std::vector<Wire*> inputs = {}, std::vector<Wire*> outputs = {});
virtual ~NInMOutLogicGate() = default;
std::size_t getNumInputs() const override;
std::size_t getNumOutputputs() const override;
Wire* getInput(std::size_t idx) const override;
Wire* getOutput(std::size_t idx) const override;
void setAtInput(std::size_t idx, Wire* value);
void setAtOutput(std::size_t idx, Wire* value);
private:
std::size_t mNumIn{ 1 };
std::size_t mNumOut{ 1 };
std::vector<Wire*> mInputs;
std::vector<Wire*> mOutputs;
};
class TwoInOneOutLogicGate : public NInMOutLogicGate
{
public:
TwoInOneOutLogicGate(Wire* input0 = nullptr, Wire* input1 = nullptr, Wire* output = nullptr);
void setInput0(Wire* input);
void setInput1(Wire* input);
void setOutput(Wire* output);
};

View file

@ -0,0 +1,72 @@
#include "ElectronicCircuitNode.h"
#include "TerminalNode.h"
#include "WireNode.h"
#include "LogicGateNode.h"
ElectronicCircuitNode::ElectronicCircuitNode(const Transform& transform)
: AbstractVisualNode(transform)
{
}
void ElectronicCircuitNode::setContent(ElectronicCircuit* content)
{
mContent = content;
mContentDirty = true;
}
void ElectronicCircuitNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
mInputTerminalNodes.clear();
mWireNodes.clear();
mLogicGateNodes.clear();
// Layout terminals
double terminal_vertical_spacing = 100;
double terminal_left_margin = 10;
double terminal_y = 0;
for (auto terminal : mContent->getInputTerminals())
{
Point loc{ terminal_left_margin, terminal_y };
auto terminal_node = std::make_unique<TerminalNode>(Transform(loc));
terminal_node->setContent(terminal);
addChild(terminal_node.get());
mInputTerminalNodes.push_back(std::move(terminal_node));
terminal_y += terminal_vertical_spacing;
}
// Layout logic gates
double logic_gate_vertical_spacing = 100;
double logic_gate_horizontal_spacing = 100;
double gate_x = logic_gate_vertical_spacing;
double gate_y = 0;
for (auto gate : mContent->getLogicGates())
{
Point loc{ gate_x, gate_y };
auto gate_node = std::make_unique<LogicGateNode>(Transform(loc));
gate_node->setContent(gate);
addChild(gate_node.get());
mLogicGateNodes.push_back(std::move(gate_node));
gate_x += logic_gate_vertical_spacing;
gate_y += logic_gate_horizontal_spacing;
}
}
void ElectronicCircuitNode::update(SceneInfo* sceneInfo)
{
if (mContentDirty)
{
createOrUpdateGeometry(sceneInfo);
mContentDirty = false;
}
}

View file

@ -0,0 +1,29 @@
#pragma once
#include "AbstractVisualNode.h"
#include "ElectronicCircuit.h"
class WireNode;
class TerminalNode;
class LogicGateNode;
class ElectronicCircuitNode : public AbstractVisualNode
{
public:
ElectronicCircuitNode(const Transform& transform);
void setContent(ElectronicCircuit* content);
void update(SceneInfo* sceneInfo);
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo);
ElectronicCircuit* mContent{ nullptr };
bool mContentDirty{ true };
std::vector<std::unique_ptr<TerminalNode> > mInputTerminalNodes;
std::vector<std::unique_ptr<WireNode> > mWireNodes;
std::vector<std::unique_ptr<LogicGateNode> > mLogicGateNodes;
};

View file

@ -0,0 +1,49 @@
#include "LogicGateNode.h"
#include "Path.h"
#include "PathNode.h"
#include "CircleNode.h"
#include "LogicGate.h"
#include "LogicGatePrimitiveShapes.h"
LogicGateNode::LogicGateNode(const Transform& transform)
: AbstractVisualNode(transform)
{
}
void LogicGateNode::setContent(LogicGate* content)
{
mContent = content;
mContentDirty = true;
}
void LogicGateNode::update(SceneInfo* sceneInfo)
{
if (mContentDirty)
{
createOrUpdateGeometry(sceneInfo);
mContentDirty = false;
}
}
void LogicGateNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
if (!mPrimaryPath)
{
if (mContent->getGateType() == LogicGate::GateType::AND)
{
mPrimaryPath = std::make_unique<PathNode>(Transform(), LogicGatePrimitiveShapes::getAndGateShape());
}
else if (mContent->getGateType() == LogicGate::GateType::OR)
{
mPrimaryPath = std::make_unique<PathNode>(Transform(), LogicGatePrimitiveShapes::getOrGateShape());
}
if (mPrimaryPath)
{
addChild(mPrimaryPath.get());
}
}
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "AbstractVisualNode.h"
class LogicGate;
class PathNode;
class CircleNode;
class LogicGateNode : public AbstractVisualNode
{
public:
LogicGateNode(const Transform& transform);
void setContent(LogicGate* content);
void update(SceneInfo* sceneInfo);
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo);
LogicGate* mContent{ nullptr };
bool mContentDirty{ true };
std::unique_ptr<PathNode> mPrimaryPath;
std::unique_ptr<CircleNode> mNegationGlyph;
};

View file

@ -0,0 +1,11 @@
#include "LogicGatePrimitiveShapes.h"
std::string LogicGatePrimitiveShapes::getAndGateShape()
{
return "M4 8 h24 a16 16 0 0 1 0 32 h-24Z";
}
std::string LogicGatePrimitiveShapes::getOrGateShape()
{
return "M4 8 h16 q16 2 24 16 q-12 16 -24 16 h-16 q12 -16 0 -32Z";
}

View file

@ -0,0 +1,11 @@
#pragma once
#include <string>
class LogicGatePrimitiveShapes
{
public:
static std::string getAndGateShape();
static std::string getOrGateShape();
};

View file

@ -0,0 +1,35 @@
#include "TerminalNode.h"
#include "CircleNode.h"
TerminalNode::TerminalNode(const Transform& transform)
: AbstractVisualNode(transform)
{
}
void TerminalNode::setContent(Terminal* terminal)
{
mContent = terminal;
}
void TerminalNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
if (!mMarker)
{
mMarker = std::make_unique<CircleNode>(Transform{}, 5);
mMarker->setFillColor(Color(0, 0, 0));
mMarker->setHasStrokeColor(false);
addChild(mMarker.get());
}
}
void TerminalNode::update(SceneInfo* sceneInfo)
{
if (mContentDirty)
{
createOrUpdateGeometry(sceneInfo);
mContentDirty = false;
}
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "AbstractVisualNode.h"
#include "Terminal.h"
class CircleNode;
class TerminalNode : public AbstractVisualNode
{
public:
TerminalNode(const Transform& transform);
void setContent(Terminal* terminal);
void update(SceneInfo* sceneInfo) override;
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo);
std::unique_ptr<CircleNode> mMarker;
Terminal* mContent{ nullptr };
bool mContentDirty{ true };
};

View file

@ -0,0 +1,6 @@
#pragma once
class WireNode
{
};