From f8a2ce3c5997e5c9b90bb78140ce4f37716a781f Mon Sep 17 00:00:00 2001 From: jmsgrogan Date: Fri, 20 Jan 2023 16:47:39 +0000 Subject: [PATCH] Initial circuits plugin work. --- plugins/circuits/CMakeLists.txt | 17 +--- plugins/circuits/ElectronicCircuit.h | 63 ------------ plugins/circuits/resources/svg/and_gate.svg | 7 ++ plugins/circuits/src/CMakeLists.txt | 42 ++++++++ .../CircuitElement.cpp} | 0 plugins/circuits/src/CircuitElement.h | 28 ++++++ plugins/circuits/src/ElectronicCircuit.cpp | 27 +++++ plugins/circuits/src/ElectronicCircuit.h | 42 ++++++++ plugins/circuits/src/Terminal.cpp | 18 ++++ plugins/circuits/src/Terminal.h | 35 +++++++ plugins/circuits/src/TruthTable.cpp | 11 +++ plugins/circuits/src/TruthTable.h | 32 ++++++ plugins/circuits/src/Wire.cpp | 18 ++++ plugins/circuits/src/Wire.h | 26 +++++ .../circuits/src/gates/BasicLogicGates.cpp | 13 +++ plugins/circuits/src/gates/BasicLogicGates.h | 45 +++++++++ plugins/circuits/src/gates/LogicGate.cpp | 96 ++++++++++++++++++ plugins/circuits/src/gates/LogicGate.h | 78 +++++++++++++++ .../src/visuals/ElectronicCircuitNode.cpp | 72 ++++++++++++++ .../src/visuals/ElectronicCircuitNode.h | 29 ++++++ .../circuits/src/visuals/LogicGateNode.cpp | 49 ++++++++++ plugins/circuits/src/visuals/LogicGateNode.h | 25 +++++ .../src/visuals/LogicGatePrimitiveShapes.cpp | 11 +++ .../src/visuals/LogicGatePrimitiveShapes.h | 11 +++ plugins/circuits/src/visuals/TerminalNode.cpp | 35 +++++++ plugins/circuits/src/visuals/TerminalNode.h | 24 +++++ plugins/circuits/src/visuals/WireNode.cpp | 0 plugins/circuits/src/visuals/WireNode.h | 6 ++ plugins/circuits/test/CMakeLists.txt | 9 ++ .../circuits/test/TestElectronicCircuit.cpp | 71 ++++++++++++++ src/base/geometry/CMakeLists.txt | 8 ++ src/base/geometry/path/Arc.cpp | 74 ++++++++++++++ src/base/geometry/path/Arc.h | 35 +++++++ src/base/geometry/path/CubicBezierCurve.cpp | 65 ++++++++++++ src/base/geometry/path/CubicBezierCurve.h | 32 ++++++ src/base/geometry/path/Curve.h | 6 +- src/base/geometry/path/Line.cpp | 24 ++++- src/base/geometry/path/Line.h | 2 +- src/base/geometry/path/LineSegment.cpp | 48 ++++++++- src/base/geometry/path/LineSegment.h | 12 ++- src/base/geometry/path/PathElement.h | 16 ++- .../geometry/path/PathPostScriptConverter.cpp | 98 ++++++++++++++++++- .../geometry/path/PathPostScriptConverter.h | 6 ++ .../geometry/path/QuadraticBezierCurve.cpp | 58 +++++++++++ src/base/geometry/path/QuadraticBezierCurve.h | 31 ++++++ src/base/geometry/points/Point.cpp | 4 +- src/base/geometry/points/Point.h | 3 +- src/base/geometry/points/PointCollection.h | 2 + src/base/geometry/points/PointParser.cpp | 66 +++++++++++++ src/base/geometry/points/PointParser.h | 18 ++++ 50 files changed, 1451 insertions(+), 97 deletions(-) delete mode 100644 plugins/circuits/ElectronicCircuit.h create mode 100644 plugins/circuits/resources/svg/and_gate.svg create mode 100644 plugins/circuits/src/CMakeLists.txt rename plugins/circuits/{ElectronicCircuit.cpp => src/CircuitElement.cpp} (100%) create mode 100644 plugins/circuits/src/CircuitElement.h create mode 100644 plugins/circuits/src/ElectronicCircuit.cpp create mode 100644 plugins/circuits/src/ElectronicCircuit.h create mode 100644 plugins/circuits/src/Terminal.cpp create mode 100644 plugins/circuits/src/Terminal.h create mode 100644 plugins/circuits/src/TruthTable.cpp create mode 100644 plugins/circuits/src/TruthTable.h create mode 100644 plugins/circuits/src/Wire.cpp create mode 100644 plugins/circuits/src/Wire.h create mode 100644 plugins/circuits/src/gates/BasicLogicGates.cpp create mode 100644 plugins/circuits/src/gates/BasicLogicGates.h create mode 100644 plugins/circuits/src/gates/LogicGate.cpp create mode 100644 plugins/circuits/src/gates/LogicGate.h create mode 100644 plugins/circuits/src/visuals/ElectronicCircuitNode.cpp create mode 100644 plugins/circuits/src/visuals/ElectronicCircuitNode.h create mode 100644 plugins/circuits/src/visuals/LogicGateNode.cpp create mode 100644 plugins/circuits/src/visuals/LogicGateNode.h create mode 100644 plugins/circuits/src/visuals/LogicGatePrimitiveShapes.cpp create mode 100644 plugins/circuits/src/visuals/LogicGatePrimitiveShapes.h create mode 100644 plugins/circuits/src/visuals/TerminalNode.cpp create mode 100644 plugins/circuits/src/visuals/TerminalNode.h create mode 100644 plugins/circuits/src/visuals/WireNode.cpp create mode 100644 plugins/circuits/src/visuals/WireNode.h create mode 100644 plugins/circuits/test/CMakeLists.txt create mode 100644 plugins/circuits/test/TestElectronicCircuit.cpp create mode 100644 src/base/geometry/path/Arc.cpp create mode 100644 src/base/geometry/path/Arc.h create mode 100644 src/base/geometry/path/CubicBezierCurve.cpp create mode 100644 src/base/geometry/path/CubicBezierCurve.h create mode 100644 src/base/geometry/path/QuadraticBezierCurve.cpp create mode 100644 src/base/geometry/path/QuadraticBezierCurve.h create mode 100644 src/base/geometry/points/PointParser.cpp create mode 100644 src/base/geometry/points/PointParser.h diff --git a/plugins/circuits/CMakeLists.txt b/plugins/circuits/CMakeLists.txt index de5d021..47f2510 100644 --- a/plugins/circuits/CMakeLists.txt +++ b/plugins/circuits/CMakeLists.txt @@ -1,15 +1,2 @@ -set(PLUGIN_NAME circuits) - -list(APPEND client_HEADERS - ElectronicCircuit.h) - -list(APPEND client_LIB_INCLUDES - ElectronicCircuit.cpp) - -add_library(${PLUGIN_NAME} SHARED ${client_LIB_INCLUDES}) - -target_include_directories(${PLUGIN_NAME} PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} - ) -target_link_libraries(${PLUGIN_NAME} PUBLIC core) -set_property(TARGET ${PLUGIN_NAME} PROPERTY FOLDER plugins) +add_subdirectory(src) +add_subdirectory(test) diff --git a/plugins/circuits/ElectronicCircuit.h b/plugins/circuits/ElectronicCircuit.h deleted file mode 100644 index 01d4662..0000000 --- a/plugins/circuits/ElectronicCircuit.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include - -class CircuitElement -{ - -}; - -class Terminal : public CircuitElement -{ -public: - enum class Type - { - INPUT, - OUTPUT - }; - - Terminal(Type type) - : mType(type) - { - - } - -private: - double mValue{0}; - Type mType; -}; - -class Wire -{ -public: - Wire(CircuitElement* input, CircuitElement* output) - : mInput(input), - mOutput(output) - { - - } - -private: - CircuitElement* mInput{nullptr}; - CircuitElement* mOutput{nullptr}; -}; - -class LogicGate -{ -public: - enum class Type - { - NOT, - AND, - OR, - XOR, - - - }; -}; - -class ElectronicCircuit -{ - -}; diff --git a/plugins/circuits/resources/svg/and_gate.svg b/plugins/circuits/resources/svg/and_gate.svg new file mode 100644 index 0000000..a4db3a2 --- /dev/null +++ b/plugins/circuits/resources/svg/and_gate.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/plugins/circuits/src/CMakeLists.txt b/plugins/circuits/src/CMakeLists.txt new file mode 100644 index 0000000..e878dd8 --- /dev/null +++ b/plugins/circuits/src/CMakeLists.txt @@ -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 ) diff --git a/plugins/circuits/ElectronicCircuit.cpp b/plugins/circuits/src/CircuitElement.cpp similarity index 100% rename from plugins/circuits/ElectronicCircuit.cpp rename to plugins/circuits/src/CircuitElement.cpp diff --git a/plugins/circuits/src/CircuitElement.h b/plugins/circuits/src/CircuitElement.h new file mode 100644 index 0000000..03fe480 --- /dev/null +++ b/plugins/circuits/src/CircuitElement.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + + +class CircuitElement +{ +public: + enum class Type + { + INPUT_TERMINAL, + OUTPUT_TERMINAL, + WIRE, + LOGIC_GATE, + FANOUT, + UNKNOWN + }; + + virtual ~CircuitElement() = default; + + virtual CircuitElement::Type getType() const = 0; +}; + + + + + diff --git a/plugins/circuits/src/ElectronicCircuit.cpp b/plugins/circuits/src/ElectronicCircuit.cpp new file mode 100644 index 0000000..ed2edd3 --- /dev/null +++ b/plugins/circuits/src/ElectronicCircuit.cpp @@ -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)); +} \ No newline at end of file diff --git a/plugins/circuits/src/ElectronicCircuit.h b/plugins/circuits/src/ElectronicCircuit.h new file mode 100644 index 0000000..f2fcd95 --- /dev/null +++ b/plugins/circuits/src/ElectronicCircuit.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +#include "CircuitElement.h" +#include "LogicGate.h" +#include "Terminal.h" + +using LogicGatePtr = std::unique_ptr; + +class ElectronicCircuit +{ +public: + void addInputTerminal(TerminalPtr terminal); + + void addOutputTerminal(TerminalPtr terminal); + + void addWire(WirePtr wire); + + void addLogicGate(LogicGatePtr gate); + + const std::vector& getInputTerminals() const + { + return mInputTerminals; + } + + const std::vector& getLogicGates() const + { + return mLogicGates; + } + +private: + std::vector mInputTerminals; + std::vector mOutputTerminals; + + std::vector mWires; + + std::vector mLogicGates; + + std::vector > mElements; +}; diff --git a/plugins/circuits/src/Terminal.cpp b/plugins/circuits/src/Terminal.cpp new file mode 100644 index 0000000..409c96b --- /dev/null +++ b/plugins/circuits/src/Terminal.cpp @@ -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; +} \ No newline at end of file diff --git a/plugins/circuits/src/Terminal.h b/plugins/circuits/src/Terminal.h new file mode 100644 index 0000000..edc6c87 --- /dev/null +++ b/plugins/circuits/src/Terminal.h @@ -0,0 +1,35 @@ +#pragma once + +#include "CircuitElement.h" + +#include + +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; \ No newline at end of file diff --git a/plugins/circuits/src/TruthTable.cpp b/plugins/circuits/src/TruthTable.cpp new file mode 100644 index 0000000..6b20b5f --- /dev/null +++ b/plugins/circuits/src/TruthTable.cpp @@ -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; +} + diff --git a/plugins/circuits/src/TruthTable.h b/plugins/circuits/src/TruthTable.h new file mode 100644 index 0000000..fb445b7 --- /dev/null +++ b/plugins/circuits/src/TruthTable.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +class TruthTable +{ +public: + using TableData = std::map, std::vector >; + + 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; +}; \ No newline at end of file diff --git a/plugins/circuits/src/Wire.cpp b/plugins/circuits/src/Wire.cpp new file mode 100644 index 0000000..c883ef9 --- /dev/null +++ b/plugins/circuits/src/Wire.cpp @@ -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; +} \ No newline at end of file diff --git a/plugins/circuits/src/Wire.h b/plugins/circuits/src/Wire.h new file mode 100644 index 0000000..b1dc23d --- /dev/null +++ b/plugins/circuits/src/Wire.h @@ -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; + +class Fanout : public CircuitElement +{ + +}; \ No newline at end of file diff --git a/plugins/circuits/src/gates/BasicLogicGates.cpp b/plugins/circuits/src/gates/BasicLogicGates.cpp new file mode 100644 index 0000000..11959b3 --- /dev/null +++ b/plugins/circuits/src/gates/BasicLogicGates.cpp @@ -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) +{ + +} \ No newline at end of file diff --git a/plugins/circuits/src/gates/BasicLogicGates.h b/plugins/circuits/src/gates/BasicLogicGates.h new file mode 100644 index 0000000..4b6a2f6 --- /dev/null +++ b/plugins/circuits/src/gates/BasicLogicGates.h @@ -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) }; +}; \ No newline at end of file diff --git a/plugins/circuits/src/gates/LogicGate.cpp b/plugins/circuits/src/gates/LogicGate.cpp new file mode 100644 index 0000000..97892af --- /dev/null +++ b/plugins/circuits/src/gates/LogicGate.cpp @@ -0,0 +1,96 @@ +#include "LogicGate.h" + +NInMOutLogicGate::NInMOutLogicGate(std::size_t numIn, std::size_t numOut, std::vector inputs, std::vector outputs) + : LogicGate(), + mNumIn(numIn), + mNumOut(numOut) +{ + if (inputs.size() == mNumIn) + { + mInputs = inputs; + } + else + { + mInputs = std::vector(numIn, nullptr); + } + + if (outputs.size() == mNumOut) + { + mOutputs = outputs; + } + else + { + mOutputs = std::vector(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); +} \ No newline at end of file diff --git a/plugins/circuits/src/gates/LogicGate.h b/plugins/circuits/src/gates/LogicGate.h new file mode 100644 index 0000000..2e242b7 --- /dev/null +++ b/plugins/circuits/src/gates/LogicGate.h @@ -0,0 +1,78 @@ +#pragma once + +#include "CircuitElement.h" +#include "TruthTable.h" +#include "Wire.h" + +#include +#include + +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 inputs = {}, std::vector 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 mInputs; + std::vector 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); +}; \ No newline at end of file diff --git a/plugins/circuits/src/visuals/ElectronicCircuitNode.cpp b/plugins/circuits/src/visuals/ElectronicCircuitNode.cpp new file mode 100644 index 0000000..9438c9e --- /dev/null +++ b/plugins/circuits/src/visuals/ElectronicCircuitNode.cpp @@ -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(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(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; + } +} \ No newline at end of file diff --git a/plugins/circuits/src/visuals/ElectronicCircuitNode.h b/plugins/circuits/src/visuals/ElectronicCircuitNode.h new file mode 100644 index 0000000..94e4d75 --- /dev/null +++ b/plugins/circuits/src/visuals/ElectronicCircuitNode.h @@ -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 > mInputTerminalNodes; + std::vector > mWireNodes; + std::vector > mLogicGateNodes; +}; \ No newline at end of file diff --git a/plugins/circuits/src/visuals/LogicGateNode.cpp b/plugins/circuits/src/visuals/LogicGateNode.cpp new file mode 100644 index 0000000..db443ad --- /dev/null +++ b/plugins/circuits/src/visuals/LogicGateNode.cpp @@ -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(Transform(), LogicGatePrimitiveShapes::getAndGateShape()); + } + else if (mContent->getGateType() == LogicGate::GateType::OR) + { + mPrimaryPath = std::make_unique(Transform(), LogicGatePrimitiveShapes::getOrGateShape()); + } + + if (mPrimaryPath) + { + addChild(mPrimaryPath.get()); + } + } +} \ No newline at end of file diff --git a/plugins/circuits/src/visuals/LogicGateNode.h b/plugins/circuits/src/visuals/LogicGateNode.h new file mode 100644 index 0000000..b4d2ccf --- /dev/null +++ b/plugins/circuits/src/visuals/LogicGateNode.h @@ -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 mPrimaryPath; + std::unique_ptr mNegationGlyph; +}; \ No newline at end of file diff --git a/plugins/circuits/src/visuals/LogicGatePrimitiveShapes.cpp b/plugins/circuits/src/visuals/LogicGatePrimitiveShapes.cpp new file mode 100644 index 0000000..e662774 --- /dev/null +++ b/plugins/circuits/src/visuals/LogicGatePrimitiveShapes.cpp @@ -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"; +} \ No newline at end of file diff --git a/plugins/circuits/src/visuals/LogicGatePrimitiveShapes.h b/plugins/circuits/src/visuals/LogicGatePrimitiveShapes.h new file mode 100644 index 0000000..88d9e63 --- /dev/null +++ b/plugins/circuits/src/visuals/LogicGatePrimitiveShapes.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class LogicGatePrimitiveShapes +{ +public: + static std::string getAndGateShape(); + + static std::string getOrGateShape(); +}; \ No newline at end of file diff --git a/plugins/circuits/src/visuals/TerminalNode.cpp b/plugins/circuits/src/visuals/TerminalNode.cpp new file mode 100644 index 0000000..0fde36e --- /dev/null +++ b/plugins/circuits/src/visuals/TerminalNode.cpp @@ -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(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; + } +} \ No newline at end of file diff --git a/plugins/circuits/src/visuals/TerminalNode.h b/plugins/circuits/src/visuals/TerminalNode.h new file mode 100644 index 0000000..57bc652 --- /dev/null +++ b/plugins/circuits/src/visuals/TerminalNode.h @@ -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 mMarker; + + Terminal* mContent{ nullptr }; + bool mContentDirty{ true }; +}; \ No newline at end of file diff --git a/plugins/circuits/src/visuals/WireNode.cpp b/plugins/circuits/src/visuals/WireNode.cpp new file mode 100644 index 0000000..e69de29 diff --git a/plugins/circuits/src/visuals/WireNode.h b/plugins/circuits/src/visuals/WireNode.h new file mode 100644 index 0000000..abed051 --- /dev/null +++ b/plugins/circuits/src/visuals/WireNode.h @@ -0,0 +1,6 @@ +#pragma once + +class WireNode +{ + +}; \ No newline at end of file diff --git a/plugins/circuits/test/CMakeLists.txt b/plugins/circuits/test/CMakeLists.txt new file mode 100644 index 0000000..6a27c5e --- /dev/null +++ b/plugins/circuits/test/CMakeLists.txt @@ -0,0 +1,9 @@ +set(PLUGIN_NAME circuits) + +list(APPEND UNIT_TEST_FILES + TestElectronicCircuit.cpp +) + +add_executable(${PLUGIN_NAME}_unit_tests ${CMAKE_SOURCE_DIR}/test/test_runner.cpp ${UNIT_TEST_FILES}) +target_link_libraries(${PLUGIN_NAME}_unit_tests PUBLIC test_utils circuits) +set_property(TARGET ${PLUGIN_NAME}_unit_tests PROPERTY FOLDER plugins) \ No newline at end of file diff --git a/plugins/circuits/test/TestElectronicCircuit.cpp b/plugins/circuits/test/TestElectronicCircuit.cpp new file mode 100644 index 0000000..75c081d --- /dev/null +++ b/plugins/circuits/test/TestElectronicCircuit.cpp @@ -0,0 +1,71 @@ +#include "TestFramework.h" +#include "TestUtils.h" +#include "TestRenderUtils.h" + +#include "ElectronicCircuit.h" +#include "ElectronicCircuitNode.h" + +#include "BasicLogicGates.h" + +TEST_CASE(TestElectronicCircuit, "circuits") +{ + TestRenderer renderer(100, 100); + + auto circuit = std::make_unique(); + + // Add three labelled terminals (two in, one out) + auto input0 = std::make_unique(Terminal::TerminalType::INPUT, "p"); + auto input1 = std::make_unique(Terminal::TerminalType::INPUT, "q"); + auto input2 = std::make_unique(Terminal::TerminalType::INPUT, "r"); + + auto output = std::make_unique(Terminal::TerminalType::OUTPUT, "s"); + + // Add gates + auto and_gate = std::make_unique(); + auto or_gate = std::make_unique(); + + // Add wires + auto wire0 = std::make_unique(input0.get(), and_gate.get()); + auto wire1 = std::make_unique(input1.get(), and_gate.get()); + + auto wire2 = std::make_unique(and_gate.get(), or_gate.get()); + auto wire3 = std::make_unique(input2.get(), or_gate.get()); + + auto wire4 = std::make_unique(or_gate.get(), output.get()); + + // Join wires + and_gate->setInput0(wire0.get()); + and_gate->setInput1(wire1.get()); + and_gate->setOutput(wire2.get()); + + or_gate->setInput0(wire2.get()); + or_gate->setInput1(wire3.get()); + or_gate->setOutput(wire4.get()); + + input0->setConnection(wire0.get()); + input1->setConnection(wire1.get()); + input2->setConnection(wire3.get()); + output->setConnection(wire4.get()); + + // Add to circuit + circuit->addWire(std::move(wire0)); + circuit->addWire(std::move(wire1)); + circuit->addWire(std::move(wire2)); + circuit->addWire(std::move(wire3)); + + circuit->addInputTerminal(std::move(input0)); + circuit->addInputTerminal(std::move(input1)); + circuit->addInputTerminal(std::move(input2)); + + circuit->addOutputTerminal(std::move(output)); + + circuit->addLogicGate(std::move(and_gate)); + circuit->addLogicGate(std::move(or_gate)); + + auto circuit_node = std::make_unique(Transform()); + + circuit_node->setContent(circuit.get()); + + renderer.getScene()->addNode(circuit_node.get()); + renderer.writeSvg(TestUtils::getTestOutputDir(__FILE__) / "circuit.svg"); +} diff --git a/src/base/geometry/CMakeLists.txt b/src/base/geometry/CMakeLists.txt index 7932c63..b92f64a 100644 --- a/src/base/geometry/CMakeLists.txt +++ b/src/base/geometry/CMakeLists.txt @@ -16,7 +16,11 @@ list(APPEND HEADERS path/Path.h path/PathPostScriptConverter.h path/PathElement.h + path/Arc.h + path/QuadraticBezierCurve.h + path/CubicBezierCurve.h points/Point.h + points/PointParser.h points/PointCollection.h points/DiscretePoint.h primitives/Circle.h @@ -37,7 +41,11 @@ list(APPEND SOURCES path/Path.cpp path/PathPostScriptConverter.cpp path/PathElement.cpp + path/Arc.cpp + path/QuadraticBezierCurve.cpp + path/CubicBezierCurve.cpp points/Point.cpp + points/PointParser.cpp points/PointCollection.cpp points/DiscretePoint.cpp primitives/Circle.cpp diff --git a/src/base/geometry/path/Arc.cpp b/src/base/geometry/path/Arc.cpp new file mode 100644 index 0000000..68492a1 --- /dev/null +++ b/src/base/geometry/path/Arc.cpp @@ -0,0 +1,74 @@ +#include "Arc.h" + +#include "PointParser.h" +#include + +Arc::Arc(const Point& startPoint, const Point& endPoint, double rX, double rY, double rotation, bool largeArc, bool sweep) + : mStartPoint(startPoint), + mEndPoint(endPoint), + mRx(rX), + mRy(rY), + mRotation(rotation), + mLargeArc(largeArc), + mSweep(sweep) +{ + +} + +Point Arc::getFirstPoint() const +{ + return mStartPoint; +} + +Point Arc::getEndPoint() const +{ + return mEndPoint; +} + +std::string Arc::toPostScriptString(std::size_t precision) const +{ + const auto large = mLargeArc ? "1" : "0"; + const auto sweep = mSweep ? "1" : "0"; + std::stringstream sstr; + if (precision > 0) + { + sstr.precision(precision); + } + sstr << (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO) ? "a" : "A"; + sstr << mRx << " " << mRy << " " << mRotation << " " << large << " " << sweep << " "; + + if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO) + { + sstr << PointParser::toStringRelative(mEndPoint, mStartPoint, 2, " ", precision); + } + else + { + sstr << PointParser::toString(mEndPoint, 2, " ", precision); + } + return sstr.str(); +} + +Bounds Arc::getBounds() const +{ + return {}; +} + +const Point& Arc::getLocation() const +{ + return mStartPoint; +} + +void Arc::sample(SparseGrid* grid) const +{ + +} + +Arc::Type Arc::getType() const +{ + return Type::CURVE; +} + +Arc::CurveType Arc::getCurveType() const +{ + return CurveType::ARC; +} \ No newline at end of file diff --git a/src/base/geometry/path/Arc.h b/src/base/geometry/path/Arc.h new file mode 100644 index 0000000..377ab64 --- /dev/null +++ b/src/base/geometry/path/Arc.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Curve.h" +#include "Point.h" + +class Arc : public Curve +{ +public: + Arc(const Point& startPoint, const Point& endPoint, double rX, double rY, double rotation = 0, bool largeArc = false, bool sweep = false); + + Point getFirstPoint() const override; + + Point getEndPoint() const override; + + Bounds getBounds() const override; + + const Point& getLocation() const override; + + Type getType() const override; + + CurveType getCurveType() const override; + + void sample(SparseGrid* grid) const override; + + std::string toPostScriptString(std::size_t precision = 0) const override; + +private: + Point mStartPoint; + Point mEndPoint; + double mRx{ 0.0 }; + double mRy{ 0.0 }; + double mRotation{ 0 }; + bool mLargeArc{ false }; + bool mSweep{ false }; +}; \ No newline at end of file diff --git a/src/base/geometry/path/CubicBezierCurve.cpp b/src/base/geometry/path/CubicBezierCurve.cpp new file mode 100644 index 0000000..7590ceb --- /dev/null +++ b/src/base/geometry/path/CubicBezierCurve.cpp @@ -0,0 +1,65 @@ +#include "CubicBezierCurve.h" + +#include "PointParser.h" + +CubicBezierCurve::CubicBezierCurve(const Point& startPoint, const Point& endPoint, const Point& startControlPoint, const Point& endControlPoint) + : mStartPoint(startPoint), + mEndPoint(endPoint), + mStartControlPoint(startControlPoint), + mEndControlPoint(endControlPoint) +{ + +} + +Point CubicBezierCurve::getFirstPoint() const +{ + return mStartPoint; +} + +Point CubicBezierCurve::getEndPoint() const +{ + return mEndPoint; +} + +std::string CubicBezierCurve::toPostScriptString(std::size_t precision) const +{ + if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO) + { + const auto start_control = PointParser::toStringRelative(mStartControlPoint, mStartPoint, 2, " ", precision); + const auto end_control = PointParser::toStringRelative(mEndControlPoint, mStartPoint, 2, " ", precision); + const auto end = PointParser::toStringRelative(mEndPoint, mStartPoint, 2, " ", precision); + return "c" + start_control + " " + end_control + " " + end; + } + else + { + const auto start_control = PointParser::toString(mStartControlPoint, 2, " ", precision); + const auto end_control = PointParser::toString(mEndControlPoint, 2, " ", precision); + const auto end = PointParser::toString(mEndPoint, 2, " ", precision); + return "C" + start_control + " " + end_control + " " + end; + } +} + +Bounds CubicBezierCurve::getBounds() const +{ + return {}; +} + +const Point& CubicBezierCurve::getLocation() const +{ + return mStartPoint; +} + +void CubicBezierCurve::sample(SparseGrid* grid) const +{ + +} + +CubicBezierCurve::Type CubicBezierCurve::getType() const +{ + return Type::CURVE; +} + +CubicBezierCurve::CurveType CubicBezierCurve::getCurveType() const +{ + return CurveType::CUBIC_BEZIER; +} \ No newline at end of file diff --git a/src/base/geometry/path/CubicBezierCurve.h b/src/base/geometry/path/CubicBezierCurve.h new file mode 100644 index 0000000..e081198 --- /dev/null +++ b/src/base/geometry/path/CubicBezierCurve.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Curve.h" +#include "Point.h" + +class CubicBezierCurve : public Curve +{ +public: + CubicBezierCurve(const Point& startPoint, const Point& endPoint, const Point& startControlPoint, const Point& endControlPoint); + + Point getFirstPoint() const override; + + Point getEndPoint() const override; + + Bounds getBounds() const override; + + const Point& getLocation() const override; + + Type getType() const override; + + CurveType getCurveType() const override; + + void sample(SparseGrid* grid) const override; + + std::string toPostScriptString(std::size_t precision = 0) const override; + +private: + Point mStartPoint; + Point mEndPoint; + Point mStartControlPoint; + Point mEndControlPoint; +}; \ No newline at end of file diff --git a/src/base/geometry/path/Curve.h b/src/base/geometry/path/Curve.h index b3f19d1..9c39946 100644 --- a/src/base/geometry/path/Curve.h +++ b/src/base/geometry/path/Curve.h @@ -8,6 +8,10 @@ public: enum class CurveType { ARC, - CUBIC_BEZIER + QUADRATIC_BEZIER, + CUBIC_BEZIER, + UNKNOWN }; + + virtual CurveType getCurveType() const = 0; }; \ No newline at end of file diff --git a/src/base/geometry/path/Line.cpp b/src/base/geometry/path/Line.cpp index c42e81a..2fc5c88 100644 --- a/src/base/geometry/path/Line.cpp +++ b/src/base/geometry/path/Line.cpp @@ -1,5 +1,7 @@ #include "Line.h" +#include "PointParser.h" + Line::Line(const Point& start, const PointCollection& points) : mStartPoint(start), mPoints(points) @@ -62,14 +64,26 @@ Line::Line(const Point& start, InputBufferType bufferType, const std::vector* grid) const override {}; - std::string toPostScriptString() const override; + std::string toPostScriptString(std::size_t precision = 0) const override; private: Point mStartPoint; diff --git a/src/base/geometry/path/LineSegment.cpp b/src/base/geometry/path/LineSegment.cpp index 9c240dd..a1ab353 100644 --- a/src/base/geometry/path/LineSegment.cpp +++ b/src/base/geometry/path/LineSegment.cpp @@ -1,5 +1,7 @@ #include "LineSegment.h" +#include "PointParser.h" + LineSegment::LineSegment(const Point& p0, const Point& p1) : mP0(p0), mP1(p1) @@ -32,9 +34,51 @@ void LineSegment::sample(SparseGrid* grid) const } -std::string LineSegment::toPostScriptString() const +std::string LineSegment::toPostScriptString(std::size_t precision) const { - return "L " + std::to_string(mP1.getX()) + " " + std::to_string(mP1.getY()); + if (isHorizontal()) + { + if (mPostscriptPositioning == PostscriptPositioning::ABSOLUTE_TO) + { + return "H" + PointParser::toString(mP1.getX(), precision); + } + else + { + return "h" + PointParser::toString(mP0.getDeltaX(mP1), precision); + } + } + else if (isVertical()) + { + if (mPostscriptPositioning == PostscriptPositioning::ABSOLUTE_TO) + { + return "V" + PointParser::toString(mP1.getY(), precision); + } + else + { + return "v" + PointParser::toString(mP0.getDeltaY(mP1), precision); + } + } + else + { + if (mPostscriptPositioning == PostscriptPositioning::ABSOLUTE_TO) + { + return "L" + PointParser::toString(mP1, 2, " ", precision); + } + else + { + return "l" + PointParser::toStringRelative(mP1, mP0, 2, " ", precision); + } + } +} + +bool LineSegment::isHorizontal() const +{ + return mP0.getDeltaY(mP1) == 0.0; +} + +bool LineSegment::isVertical() const +{ + return mP0.getDeltaX(mP1) == 0.0; } Bounds LineSegment::getBounds() const diff --git a/src/base/geometry/path/LineSegment.h b/src/base/geometry/path/LineSegment.h index 98c6861..67d200f 100644 --- a/src/base/geometry/path/LineSegment.h +++ b/src/base/geometry/path/LineSegment.h @@ -17,8 +17,6 @@ public: const Point& getPoint1() const; - void sample(SparseGrid* grid) const override; - Bounds getBounds() const override; const Point& getLocation() const override; @@ -27,10 +25,16 @@ public: Point getEndPoint() const override; - std::string toPostScriptString() const override; - Type getType() const override; + bool isHorizontal() const; + + bool isVertical() const; + + void sample(SparseGrid* grid) const override; + + std::string toPostScriptString(std::size_t precision = 0) const override; + private: Point mP0; Point mP1; diff --git a/src/base/geometry/path/PathElement.h b/src/base/geometry/path/PathElement.h index bb9672a..c1abdeb 100644 --- a/src/base/geometry/path/PathElement.h +++ b/src/base/geometry/path/PathElement.h @@ -7,11 +7,25 @@ class PathElement : public AbstractGeometricItem { public: + enum class PostscriptPositioning + { + RELATIVE_TO, + ABSOLUTE_TO + }; + ~PathElement(); virtual Point getFirstPoint() const = 0; virtual Point getEndPoint() const = 0; - virtual std::string toPostScriptString() const = 0; + void setPostscriptPositioning(PostscriptPositioning positioning) + { + mPostscriptPositioning = positioning; + } + + virtual std::string toPostScriptString(std::size_t precision = 0) const = 0; + +protected: + PostscriptPositioning mPostscriptPositioning{ PostscriptPositioning::RELATIVE_TO}; }; \ No newline at end of file diff --git a/src/base/geometry/path/PathPostScriptConverter.cpp b/src/base/geometry/path/PathPostScriptConverter.cpp index 62313f1..8b92ade 100644 --- a/src/base/geometry/path/PathPostScriptConverter.cpp +++ b/src/base/geometry/path/PathPostScriptConverter.cpp @@ -7,6 +7,9 @@ #include "Line.h" #include "LineSegment.h" +#include "Arc.h" +#include "QuadraticBezierCurve.h" +#include "CubicBezierCurve.h" void PathPostScriptConverter::fromPostScript(GeometryPath* targetPath, const std::string& postScriptPath) { @@ -118,6 +121,18 @@ void PathPostScriptConverter::onElementEnd() { element = onLineTo(); } + else if (mLineState == LineState::IN_ARC) + { + element = onArc(); + } + else if (mLineState == LineState::IN_QUADRATIC_BEZIER) + { + element = onQuadraticBezier(); + } + else if (mLineState == LineState::IN_CUBIC_BEZIER) + { + element = onCubicBezier(); + } else if (mLineState == LineState::IN_FIRST_POINT) { onMoveTo(); @@ -125,6 +140,8 @@ void PathPostScriptConverter::onElementEnd() if (element) { + const auto positioning = (mPositionState == PositionState::RELATIVE) ? PathElement::PostscriptPositioning::RELATIVE_TO : PathElement::PostscriptPositioning::ABSOLUTE_TO; + element->setPostscriptPositioning(positioning); mCurrentPoint = element->getEndPoint(); mWorkingFeature->addElement(std::move(element)); } @@ -216,6 +233,85 @@ PathElementPtr PathPostScriptConverter::onLineTo() return element; } +PathElementPtr PathPostScriptConverter::onArc() +{ + PathElementPtr element; + if (mPointBuffer.size() == 7) + { + double rx = mPointBuffer[0]; + double ry = mPointBuffer[1]; + double rotation = mPointBuffer[2]; + bool large_arc = bool(mPointBuffer[3]); + bool sweep = bool(mPointBuffer[4]); + + if (mPositionState == PositionState::RELATIVE) + { + const auto end_point = Point(mCurrentPoint.getX() + mPointBuffer[5], mCurrentPoint.getY() + mPointBuffer[6]); + element = std::make_unique(mCurrentPoint, end_point, rx, ry, rotation, large_arc, sweep); + } + else + { + const auto end_point = Point(mPointBuffer[5], mPointBuffer[6]); + element = std::make_unique(mCurrentPoint, end_point, rx, ry, rotation, large_arc, sweep); + } + } + return element; +} + +PathElementPtr PathPostScriptConverter::onQuadraticBezier() +{ + PathElementPtr element; + if (mPointBuffer.size() == 4) + { + double control_x = mPointBuffer[0]; + double control_y = mPointBuffer[1]; + double end_x = mPointBuffer[2]; + bool end_y = mPointBuffer[3]; + if (mPositionState == PositionState::RELATIVE) + { + const auto control_point = Point(mCurrentPoint.getX() + control_x, mCurrentPoint.getY() + control_y); + const auto end_point = Point(mCurrentPoint.getX() + end_x, mCurrentPoint.getY() + end_y); + element = std::make_unique(mCurrentPoint, end_point, control_point); + } + else + { + const auto control_point = Point(control_x, control_y); + const auto end_point = Point(end_x, end_y); + element = std::make_unique(mCurrentPoint, end_point, control_point); + } + } + return element; +} + +PathElementPtr PathPostScriptConverter::onCubicBezier() +{ + PathElementPtr element; + if (mPointBuffer.size() == 4) + { + double control0_x = mPointBuffer[0]; + double control0_y = mPointBuffer[1]; + double control1_x = mPointBuffer[0]; + double control1_y = mPointBuffer[1]; + double end_x = mPointBuffer[2]; + bool end_y = mPointBuffer[3]; + if (mPositionState == PositionState::RELATIVE) + { + const auto control_point0 = Point(mCurrentPoint.getX() + control0_x, mCurrentPoint.getY() + control0_y); + const auto control_point1 = Point(mCurrentPoint.getX() + control1_x, mCurrentPoint.getY() + control1_y); + const auto end_point = Point(mCurrentPoint.getX() + end_x, mCurrentPoint.getY() + end_y); + element = std::make_unique(mCurrentPoint, end_point, control_point0, control_point1); + } + else + { + const auto control_point0 = Point(control0_x, control0_y); + const auto control_point1 = Point(control1_x, control1_y); + const auto end_point = Point(end_x, end_y); + element = std::make_unique(mCurrentPoint, end_point, control_point0, control_point1); + } + } + return element; +} + std::string PathPostScriptConverter::toPostScript(const GeometryPath* targetPath) { std::string path; @@ -226,7 +322,7 @@ std::string PathPostScriptConverter::toPostScript(const GeometryPath* targetPath for (const auto& path_element : feature->getElements()) { - path += " " + path_element->toPostScriptString(); + path += " " + path_element->toPostScriptString(mPrecision); } path += "Z "; } diff --git a/src/base/geometry/path/PathPostScriptConverter.h b/src/base/geometry/path/PathPostScriptConverter.h index 1be935d..a40265a 100644 --- a/src/base/geometry/path/PathPostScriptConverter.h +++ b/src/base/geometry/path/PathPostScriptConverter.h @@ -51,6 +51,10 @@ private: PathElementPtr onVerticalLineTo(); PathElementPtr onLineTo(); + PathElementPtr onArc(); + PathElementPtr onQuadraticBezier(); + PathElementPtr onCubicBezier(); + LineState mLineState{ LineState::START }; PositionState mPositionState{ PositionState::ABSOLUTE }; std::string mBuffer; @@ -58,4 +62,6 @@ private: GeometryPathFeaturePtr mWorkingFeature; Point mCurrentPoint; + + std::size_t mPrecision{ 3 }; }; diff --git a/src/base/geometry/path/QuadraticBezierCurve.cpp b/src/base/geometry/path/QuadraticBezierCurve.cpp new file mode 100644 index 0000000..1159f44 --- /dev/null +++ b/src/base/geometry/path/QuadraticBezierCurve.cpp @@ -0,0 +1,58 @@ +#include "QuadraticBezierCurve.h" + +#include "PointParser.h" + +QuadraticBezierCurve::QuadraticBezierCurve(const Point& startPoint, const Point& endPoint, const Point& controlPoint) + : mStartPoint(startPoint), + mEndPoint(endPoint), + mControlPoint(controlPoint) +{ + +} + +Point QuadraticBezierCurve::getFirstPoint() const +{ + return mStartPoint; +} + +Point QuadraticBezierCurve::getEndPoint() const +{ + return mEndPoint; +} + +std::string QuadraticBezierCurve::toPostScriptString(std::size_t precision) const +{ + if (mPostscriptPositioning == PostscriptPositioning::RELATIVE_TO) + { + return "q" + PointParser::toStringRelative(mControlPoint, mStartPoint, 2, " ", precision) + " " + PointParser::toStringRelative(mEndPoint, mStartPoint, 2, " ", precision); + } + else + { + return "Q" + PointParser::toString(mControlPoint, 2, " ", precision) + " " + PointParser::toString(mEndPoint, 2, " ", precision); + } +} + +Bounds QuadraticBezierCurve::getBounds() const +{ + return {}; +} + +const Point& QuadraticBezierCurve::getLocation() const +{ + return mStartPoint; +} + +void QuadraticBezierCurve::sample(SparseGrid* grid) const +{ + +} + +QuadraticBezierCurve::Type QuadraticBezierCurve::getType() const +{ + return Type::CURVE; +} + +QuadraticBezierCurve::CurveType QuadraticBezierCurve::getCurveType() const +{ + return CurveType::QUADRATIC_BEZIER; +} \ No newline at end of file diff --git a/src/base/geometry/path/QuadraticBezierCurve.h b/src/base/geometry/path/QuadraticBezierCurve.h new file mode 100644 index 0000000..b742fff --- /dev/null +++ b/src/base/geometry/path/QuadraticBezierCurve.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Curve.h" +#include "Point.h" + +class QuadraticBezierCurve : public Curve +{ +public: + QuadraticBezierCurve(const Point& startPoint, const Point& endPoint, const Point& controlPoint); + + Point getFirstPoint() const override; + + Point getEndPoint() const override; + + Bounds getBounds() const override; + + const Point& getLocation() const override; + + Type getType() const override; + + CurveType getCurveType() const override; + + void sample(SparseGrid* grid) const override; + + std::string toPostScriptString(std::size_t precision = 0) const override; + +private: + Point mStartPoint; + Point mEndPoint; + Point mControlPoint; +}; \ No newline at end of file diff --git a/src/base/geometry/points/Point.cpp b/src/base/geometry/points/Point.cpp index 95f4c52..ad63b11 100644 --- a/src/base/geometry/points/Point.cpp +++ b/src/base/geometry/points/Point.cpp @@ -31,9 +31,9 @@ Point::~Point() { }; -std::shared_ptr Point::Create(double x, double y, double z) +std::unique_ptr Point::Create(double x, double y, double z) { - return std::make_shared(x, y, z); + return std::make_unique(x, y, z); } double Point::getX() const diff --git a/src/base/geometry/points/Point.h b/src/base/geometry/points/Point.h index 92f8bc7..cd66be4 100644 --- a/src/base/geometry/points/Point.h +++ b/src/base/geometry/points/Point.h @@ -4,7 +4,6 @@ #include "Vector.h" #include -#include class Transform; @@ -19,7 +18,7 @@ public: ~Point(); - static std::shared_ptr Create(double x, double y, double z = 0); + static std::unique_ptr Create(double x, double y, double z = 0); void apply(const Transform& transform); diff --git a/src/base/geometry/points/PointCollection.h b/src/base/geometry/points/PointCollection.h index 18bb11d..99f917b 100644 --- a/src/base/geometry/points/PointCollection.h +++ b/src/base/geometry/points/PointCollection.h @@ -4,6 +4,8 @@ #include "Transform.h" #include "Bounds.h" +#include + class PointCollection { public: diff --git a/src/base/geometry/points/PointParser.cpp b/src/base/geometry/points/PointParser.cpp new file mode 100644 index 0000000..7fa3608 --- /dev/null +++ b/src/base/geometry/points/PointParser.cpp @@ -0,0 +1,66 @@ +#include "PointParser.h" + +#include + +std::string PointParser::toString(const Point& p, std::size_t dimensions, const std::string& delimiter, std::size_t precision) +{ + return toString(p.getX(), p.getY(), p.getZ(), dimensions, delimiter, precision); +} + +std::string PointParser::toStringRelative(const Point& p, const Point& relativeTo, std::size_t dimensions, const std::string& delimiter, std::size_t precision) +{ + return toString(relativeTo.getDeltaX(p), relativeTo.getDeltaY(p), relativeTo.getDeltaZ(p), dimensions, delimiter, precision); +} + +std::string PointParser::toString(double x, double y, double z, std::size_t dimensions, const std::string& delimiter, std::size_t precision) +{ + if (precision == 0) + { + if (dimensions == 1) + { + return std::to_string(x); + } + else if (dimensions == 2) + { + return std::to_string(x) + delimiter + std::to_string(y); + } + else + { + return std::to_string(x) + delimiter + std::to_string(y) + delimiter + std::to_string(z); + } + } + else + { + std::stringstream sstr; + sstr.precision(precision); + if (dimensions == 1) + { + sstr << x; + } + else if (dimensions == 2) + { + sstr << x << delimiter << y; + } + else + { + sstr << x << delimiter << y << delimiter << z; + } + return sstr.str(); + } +} + +std::string PointParser::toString(double x, std::size_t precision) +{ + if (precision == 0) + { + return std::to_string(x); + } + else + { + std::stringstream sstr; + sstr.precision(precision); + sstr << x; + return sstr.str(); + } +} + diff --git a/src/base/geometry/points/PointParser.h b/src/base/geometry/points/PointParser.h new file mode 100644 index 0000000..4c2f1b1 --- /dev/null +++ b/src/base/geometry/points/PointParser.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Point.h" + +#include + +class PointParser +{ +public: + + static std::string toString(const Point& p, std::size_t dimensions = 3, const std::string& delimiter = " ", std::size_t precision = 0); + + static std::string toStringRelative(const Point& p, const Point& relativeTo, std::size_t dimensions = 3, const std::string& delimiter = " ", std::size_t precision = 0); + + static std::string toString(double x, double y, double z, std::size_t dimensions = 3, const std::string& delimiter = " ", std::size_t precision = 0); + + static std::string toString(double x, std::size_t precision = 0); +};