This commit is contained in:
Tobias P.L Wennberg 2025-08-26 09:41:07 +02:00
parent 03c2e42aa8
commit 0d4dd98501
17 changed files with 419 additions and 217 deletions

View File

@ -1,7 +1,8 @@
# Makefile for ImGui SDL2 + OpenGL3 project
CXX = g++
CXXFLAGS = -std=c++26 -Wall -g
CXXFLAGS = -std=c++26 -Wall -g
#CXXFLAGS = -std=c++26 -Wall -g -Werror
INCLUDE_SOURCES = \
include/imgui/imgui.cpp \
@ -16,7 +17,7 @@ INCLUDE_SOURCES = \
include/implot/implot_items.cpp
INCLUDES = -Iinclude -Iinclude/imgui -Iinclude/imnodes -Iinclude/implot
INCLUDES = -Iinclude -Iinclude/imgui -Iinclude/imnodes -Iinclude/implot -Isrc/world
# SDL2 and OpenGL flags
UNAME_S := $(shell uname -s)

BIN
app

Binary file not shown.

View File

@ -12,13 +12,37 @@
"-Iinclude/imgui",
"-Iinclude/imnodes",
"-Iinclude/implot",
"-Isrc/world",
"-c",
"-o",
"include/implot/implot_items.o",
"include/implot/implot_items.cpp"
"src/cables/testcable.o",
"src/cables/testcable.cpp"
],
"directory": "/home/t/org/dev/simulated_network/cpp",
"file": "/home/t/org/dev/simulated_network/cpp/include/implot/implot_items.cpp",
"output": "/home/t/org/dev/simulated_network/cpp/include/implot/implot_items.o"
"file": "/home/t/org/dev/simulated_network/cpp/src/cables/testcable.cpp",
"output": "/home/t/org/dev/simulated_network/cpp/src/cables/testcable.o"
},
{
"arguments": [
"/usr/bin/g++",
"-std=c++26",
"-Wall",
"-g",
"-I/usr/include/SDL2",
"-D_GNU_SOURCE=1",
"-D_REENTRANT",
"-Iinclude",
"-Iinclude/imgui",
"-Iinclude/imnodes",
"-Iinclude/implot",
"-Isrc/world",
"-c",
"-o",
"src/world/computer.o",
"src/world/computer.cpp"
],
"directory": "/home/t/org/dev/simulated_network/cpp",
"file": "/home/t/org/dev/simulated_network/cpp/src/world/computer.cpp",
"output": "/home/t/org/dev/simulated_network/cpp/src/world/computer.o"
}
]

View File

@ -0,0 +1,3 @@
* Conductor
The ~conductor.h~ is the file providing tooling to emulate circuits using with conductors as its medium of transfer. It simulates conductor, many cables would use multiple of these. It simulates with 1ms steps, updated using the ~.step()~ function. It merely emulates the voltage in the conductor. It does not take into account more complex concepts such as time or crosstalk.

View File

@ -0,0 +1,50 @@
#include "conductor.h"
#include <cassert>
#include <vector>
#include "implot.h"
Conductor::Conductor(int ports) {
port_voltage = std::vector<float>(ports);
DEBUG_port_voltages_set = ports;
}
void Conductor::step() {
assert((DEBUG_port_voltages_set == port_voltage.size()) && "Did not set the voltage on all conductor ports");
DEBUG_port_voltages_set = 0;
voltages_index = (voltages_index+1)%(voltages.size());
assert(voltages_index < voltages.size());
}
float Conductor::voltage(float U) {
assert((DEBUG_port_voltages_set == port_voltage.size()) && "Did not set the voltage on all conductor ports");
return U - voltages[voltages_index]/3;
}
float Conductor::voltage(int port) {
assert((DEBUG_port_voltages_set == port_voltage.size()) && "Did not set the voltage on all conductor ports");
return port_voltage[port] - voltages[voltages_index]/3;
}
void Conductor::set_voltage(int port, float U) {
DEBUG_port_voltages_set++;
assert(port < port_voltage.size());
assert(voltages_index < voltages.size());
port_voltage[port] = U;
voltages[voltages_index] = 0;
for (auto p: port_voltage) {
voltages[voltages_index] += p;
}
}
void Conductor::draw_graph_view() {
ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0));
if (ImPlot::BeginPlot("##spark",ImVec2(-1, 35),ImPlotFlags_CanvasOnly)) {
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
ImPlot::SetupAxesLimits(0, 1000 - 1, 0, 11.0f, ImGuiCond_Always);
ImPlot::SetNextLineStyle(ImPlot::GetColormapColor(2));
ImPlot::SetNextFillStyle(ImPlot::GetColormapColor(2), 0.25);
ImPlot::PlotLine("##spark", &voltages[0], 1000, 1, 0, ImPlotLineFlags_Shaded, voltages_index);
ImPlot::EndPlot();
}
ImPlot::PopStyleVar();
}

View File

@ -0,0 +1,46 @@
#pragma once
#include <vector>
#include <array>
class Conductor {
public:
/*
* ports is the number of
* hosts connected to the Conductor.
* Must be correct for the voltage
* math to get correct
*/
Conductor(int ports);
/*
* Simulation 1ms step
* Cables always go before machines
*/
void step();
/*
* Get the voltage over the port
* with corresponding voltage
*/
float voltage(float U);
float voltage(int id);
/*
* Set the voltage on the port
* For debug reasons, only run this
* function once per port per step
*/
void set_voltage(int port, float U);
/*
* Draw the graph view.
* Runs BeginPlot
*/
void draw_graph_view();
private:
std::vector<float> port_voltage;
std::array<float, 1000> voltages;
int voltages_index = 0;
unsigned long DEBUG_port_voltages_set = 0;
};

55
src/cables/testcable.cpp Normal file
View File

@ -0,0 +1,55 @@
#include "testcable.h"
#include <imgui.h>
#include <imnodes.h>
#include "world.h"
TestCable::TestCable(int t_id) : id(t_id) {
}
TestCable::~TestCable() {
}
void TestCable::draw_node() {
ImNodes::BeginNode(id);
ImNodes::BeginNodeTitleBar();
ImGui::Text("TestCable");
if(ImGui::Button("Add port")) add_port();
ImNodes::EndNodeTitleBar();
for(auto& p: ports) {
ImNodes::BeginInputAttribute(p);
ImGui::Text("TestCable port");
ImGui::Begin("My Window");
ImGui::End();
ImNodes::EndInputAttribute();
}
ImNodes::BeginOutputAttribute(0);
ImGui::Dummy(ImVec2(80.0f, 45.0f));
ImNodes::EndOutputAttribute();
ImNodes::EndNode();
}
void TestCable::add_port() {
auto shared = shared_from_this();
auto shared_cable = static_cast<std::shared_ptr<Cable>>(shared);
int id = World::add_port(shared_from_this()->weak_from_this());
ports.emplace_back(id);
}
void TestCable::inspect() {
ImGui::Text("Inspecting a cable");
}
void TestCable::step() {
}

40
src/cables/testcable.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <memory>
#include <vector>
#include "cable.h"
class TestCable : public Cable, public std::enable_shared_from_this<TestCable> {
public:
TestCable(int t_id);
/*
* Step 1ms simulation
*/
void step() override;
/*
* Draw cable node and connection
* to port
*/
void draw_node() override;
/*
* What to draw in the inspect
* window
*/
void inspect() override;
/*
* Get a shared ptr of this object
*/
std::shared_ptr<TestCable> shared_ptr();
~TestCable() override;
private:
void add_port();
std::vector<int> ports;
int id;
};

View File

@ -1,3 +1,4 @@
#include <chrono>
#include <format>
#include <imgui.h>
#include <imnodes.h>
@ -9,22 +10,12 @@
#include "world/world.h"
#include <implot.h>
template <typename T>
inline T RandomRange(T min, T max) {
T scale = rand() / (T) RAND_MAX;
return min + scale * ( max - min );
}
void Sparkline(const char* id, const float* values, int count, float min_v, float max_v, int offset, const ImVec4& col, const ImVec2& size) {
ImPlot::PushStyleVar(ImPlotStyleVar_PlotPadding, ImVec2(0,0));
if (ImPlot::BeginPlot(id,size,ImPlotFlags_CanvasOnly)) {
ImPlot::SetupAxes(nullptr,nullptr,ImPlotAxisFlags_NoDecorations,ImPlotAxisFlags_NoDecorations);
ImPlot::SetupAxesLimits(0, count - 1, min_v, max_v, ImGuiCond_Always);
ImPlot::SetNextLineStyle(col);
ImPlot::SetNextFillStyle(col, 0.25);
ImPlot::PlotLine(id, values, count, 1, 0, ImPlotLineFlags_Shaded, offset);
ImPlot::EndPlot();
}
ImPlot::PopStyleVar();
bool update_world(long last_update_time) {
float sim_speed_ms = World::simulation_speed();
float sim_seconds = sim_speed_ms * (std::clock() - last_update_time);
float sim_ms = sim_seconds/1000;
assert(sim_ms >= 0);
return sim_ms > 1;
}
int main() {
@ -105,10 +96,20 @@ int main() {
// Our state
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
/*
* IRL time
*/
auto last_step_time = std::clock();
// Main loop
bool done = false;
while (!done)
{
if (update_world(last_step_time)) {
last_step_time = std::clock();
std::println("STEP!");
World::step();
}
/***************/
/**IMGUI SETUP**/
/***************/
@ -136,42 +137,6 @@ int main() {
/**MY CODE**/
/***********/
World::draw_gui();
///////////////////////////////////
static ImGuiTableFlags flags = ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV |
ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable;
static bool anim = true;
static int offset = 0;
ImGui::BulletText("Plots can be used inside of ImGui tables as another means of creating subplots.");
ImGui::Checkbox("Animate",&anim);
if (anim)
offset = (offset + 1) % 1000;
if (ImGui::BeginTable("##table", 3, flags, ImVec2(-1,0))) {
ImGui::TableSetupColumn("Electrode", ImGuiTableColumnFlags_WidthFixed, 75.0f);
ImGui::TableSetupColumn("Voltage", ImGuiTableColumnFlags_WidthFixed, 75.0f);
ImGui::TableSetupColumn("EMG Signal");
ImGui::TableHeadersRow();
ImPlot::PushColormap(ImPlotColormap_Cool);
for (int row = 0; row < 10; row++) {
ImGui::TableNextRow();
static float data[1000];
srand(row);
for (int i = 0; i < 1000; ++i)
data[i] = RandomRange(0.0f,10.0f);
ImGui::TableSetColumnIndex(0);
ImGui::Text("EMG %d", row);
ImGui::TableSetColumnIndex(1);
ImGui::Text("%.3f V", data[offset]);
ImGui::TableSetColumnIndex(2);
ImGui::PushID(row);
Sparkline("##spark",data,1000,0,11.0f,offset,ImPlot::GetColormapColor(row),ImVec2(-1, 35));
ImGui::PopID();
}
ImPlot::PopColormap();
ImGui::EndTable();
}
///////////////////////////////////
/*******************/
/**IMGUI RENDERING**/

26
src/world/cable.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "node.h"
class Cable
: public Node {
public:
/*
* Step 1ms simulation
*/
virtual void step() = 0;
/*
* Draw cable node and connection
* to port
*/
virtual void draw_node() = 0;
/*
* What to draw in the inspect
* window
*/
virtual void inspect() = 0;
virtual ~Cable() = default;
};

50
src/world/computer.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "computer.h"
#include <imgui.h>
#include <imnodes.h>
#include "world.h"
#include <vector>
void Computer::inspect() {
ImGui::Text("Inspecting a computer");
}
void Computer::step() {
}
void Computer::draw_node() {
ImNodes::BeginNode(id);
ImNodes::BeginNodeTitleBar();
ImGui::Text("Node");
if(ImGui::Button("Add port")) add_port();
ImNodes::EndNodeTitleBar();
ImNodes::BeginInputAttribute(0);
ImGui::Dummy(ImVec2(80.0f, 45.0f));
ImNodes::EndOutputAttribute();
for(auto& p: ports) p->draw_attribute();
ImNodes::EndNode();
}
void Computer::add_port() {
int id = World::add_port(shared_from_this()->weak_from_this());
ports.emplace_back(std::make_unique<Port>(id));
}
Computer::Computer(int t_id) {
id = t_id;
}
void Port::draw_attribute() {
ImNodes::BeginOutputAttribute(id);
ImGui::Text("Output port");
ImNodes::EndOutputAttribute();
}
Port::Port(int t_id) {
id = t_id;
}

42
src/world/computer.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#include <memory>
#include <optional>
#include "cable.h"
#include <vector>
class Port {
public:
Port(int t_id);
void draw_attribute();
private:
int id;
std::optional<Cable*> cable;
};
class Computer
: public std::enable_shared_from_this<Computer>, public Node {
public:
void set_inspect_node();
void inspect();
Computer(int t_id);
/*
* The one millisecond step the
* simulation runs on
*/
void step();
/*
* Draw the computer in the node tree
* Note, you need to setup node before
* running this, and end it afterwards
*/
void draw_node();
private:
void add_port();
int id;
std::string name = "PC";
std::vector<std::unique_ptr<Port>> ports;
};

View File

@ -1,97 +0,0 @@
#include "node.h"
#include <imgui.h>
#include <imnodes.h>
#include <limits>
#include <map>
#include <memory>
#include <print>
#include <variant>
#include <vector>
#include <imnodes.h>
#include "world.h"
#include <implot.h>
void Computer::inspect() {
ImGui::Text("Inspecting a computer");
}
void Computer::step() {
}
void Computer::draw_node() {
ImNodes::BeginNode(id);
ImNodes::BeginNodeTitleBar();
ImGui::Text("Node");
if(ImGui::Button("Add port")) add_port();
ImNodes::EndNodeTitleBar();
ImNodes::BeginInputAttribute(0);
ImGui::Dummy(ImVec2(80.0f, 45.0f));
ImNodes::EndOutputAttribute();
for(auto& p: ports) p->draw_attribute();
ImNodes::EndNode();
}
void Computer::add_port() {
int id = World::add_port(shared_from_this()->weak_from_this());
ports.emplace_back(std::make_unique<Port>(id));
}
Computer::Computer(int t_id) {
id = t_id;
}
void Port::draw_attribute() {
ImNodes::BeginOutputAttribute(id);
ImGui::Text("Output port");
ImNodes::EndOutputAttribute();
}
Port::Port(int t_id) {
id = t_id;
}
Cable::Cable(int t_id) {
id = t_id;
//add_port();
//add_port();
}
void Cable::draw_node() {
ImNodes::BeginNode(id);
ImNodes::BeginNodeTitleBar();
ImGui::Text("Cable");
if(ImGui::Button("Add port")) add_port();
ImNodes::EndNodeTitleBar();
for(auto& p: ports) {
ImNodes::BeginInputAttribute(p);
ImGui::Text("Cable port");
ImGui::Begin("My Window");
ImGui::End();
ImNodes::EndInputAttribute();
}
ImNodes::BeginOutputAttribute(0);
ImGui::Dummy(ImVec2(80.0f, 45.0f));
ImNodes::EndOutputAttribute();
ImNodes::EndNode();
}
void Cable::add_port() {
int id = World::add_port(shared_from_this()->weak_from_this());
ports.emplace_back(id);
}
void Cable::inspect() {
ImGui::Text("Inspecting a cable");
}

View File

@ -1,10 +1,5 @@
#pragma once
#include <optional>
#include <string.h>
#include <memory>
#include <vector>
class Node {
public:
/*
@ -17,58 +12,12 @@ public:
* Draw the node in the node graph
*/
virtual void draw_node() = 0;
};
class Cable
: public std::enable_shared_from_this<Cable>, public Node {
public:
void inspect();
void set_inspect_node();
Cable(int t_id);
std::shared_ptr<Cable> shared_ptr();
/*
* Draw cable node and connection
* to port
*/
void draw_node();
private:
void add_port();
std::vector<int> ports;
int id;
};
class Port {
public:
Port(int t_id);
void draw_attribute();
private:
int id;
std::optional<Cable*> cable;
};
class Computer
: public std::enable_shared_from_this<Computer>, public Node {
public:
void set_inspect_node();
void inspect();
Computer(int t_id);
/*
* The one millisecond step the
* simulation runs on
*/
void step();
/*
* Draw the computer in the node tree
* Note, you need to setup node before
* running this, and end it afterwards
* Step the node forward 1ms in the
* virtualization
*/
void draw_node();
virtual void step() = 0;
private:
void add_port();
int id;
std::string name = "PC";
std::vector<std::unique_ptr<Port>> ports;
virtual ~Node() = default; // Added virtual destructor
};

View File

@ -7,6 +7,8 @@
#include <limits>
#include "world.h"
#include <print>
#include "../OSI/physical/conductor.h"
#include "../cables/testcable.h"
#define NO_ID std::numeric_limits<int>::min()
@ -44,6 +46,8 @@ namespace Lookup_table {
*/
static auto inspected_node = std::weak_ptr<Node>();
static float simulation_speed = 1.;
int get_id() {
static int id = 0;
//static int id = NO_ID;
@ -67,6 +71,16 @@ namespace Lookup_table {
ImNodes::Link(link.first, link.first, link.second);
}
}
void step_nodes() {
for(auto& index: nodes_table) {
auto variant = index.second;
if(auto a = std::get_if<std::shared_ptr<Node>>(&variant))
(*a)->step();
else if(std::holds_alternative<std::weak_ptr<Node>>(variant))
continue;
else assert(false);
}
}
}
void add_computer() {
@ -78,8 +92,8 @@ void add_computer() {
void add_cable() {
int id = Lookup_table::get_id();
auto c = std::make_shared<Cable>(id);
std::shared_ptr<Node> node = std::static_pointer_cast<Node>(c);
auto c = std::make_shared<TestCable>(id);
std::shared_ptr<Node> node = static_cast<std::shared_ptr<Cable>>(c);
Lookup_table::nodes_table.insert({id, node});
}
@ -96,13 +110,12 @@ int World::add_port(std::weak_ptr<Cable> n){
}
void World::add_link(int computer_port, int cable_port) {
if(Lookup_table::links.contains(computer_port)) {
Lookup_table::links[computer_port] = cable_port;
}
else if (auto val = std::find_if(
if (auto val = std::find_if(
Lookup_table::links.begin(), Lookup_table::links.end(), [&](const auto& val){ return val.second == cable_port; }
); val != Lookup_table::links.end()) {
Lookup_table::links.erase(val->first);
}
if(Lookup_table::links.contains(computer_port)) {
Lookup_table::links[computer_port] = cable_port;
}
else
@ -153,6 +166,12 @@ void World::set_inspect_node(int id) {
Lookup_table::inspected_node = node;
}
void World::step() {
}
Conductor conduct = Conductor(3);
void World::draw_gui() {
ImGui::Begin("Add Nodes");
ImGui::Text("Add nodes:");
@ -160,11 +179,25 @@ void World::draw_gui() {
add_computer();
if(ImGui::Button("Add Cable"))
add_cable();
ImGui::SliderFloat("Simulation Speed", &Lookup_table::simulation_speed, 0, 5);
ImGui::End();
ImGui::Begin("Test conductor");
conduct.step();
conduct.set_voltage(0, 5);
conduct.set_voltage(1, -2.5);
conduct.set_voltage(2, 1);
conduct.draw_graph_view();
ImGui::End();
draw_nodes();
draw_inspect_node();
}
float World::simulation_speed() {
return Lookup_table::simulation_speed;
}

View File

@ -1,7 +1,8 @@
#pragma once
#include <memory>
#include "node.h"
#include "computer.h"
#include "cable.h"
namespace World {
/*
* Draw the entire gui
@ -9,6 +10,19 @@ namespace World {
*/
void draw_gui();
/*
* The function that calls all steps
* and advance the simulation 1ms
*/
void step();
/*
* The relationship between simulation time
* and real world time in unit
* simulation_second/irl_second
*/
float simulation_speed();
/*
* Add a computer and cable port
* respectively. Returns it's id
@ -26,4 +40,5 @@ namespace World {
* draw inspect node
*/
void set_inspect_node(int id);
}