19 Commits

Author SHA1 Message Date
b2f27e0ec0 Update README.md 2025-09-16 23:45:24 +00:00
b4a53e4f58 Update README.md 2025-09-15 17:20:45 +00:00
fff343cdfb readme mods 2025-09-15 17:08:50 +00:00
5ccd71c285 Remove reference to git bash (no longer necessary) 2025-09-15 12:06:24 -05:00
ffd11d0995 Need the -LD flag for both configs to build a DLL. More: https://learn.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=msvc-170 2025-09-15 11:57:06 -05:00
cb6d239d00 readme mods. can build with glfw in either Debug or Release config. some reorg of the cpp example 2025-09-15 11:53:24 -05:00
6d45d12dc1 Save cs example into an actual project 2025-09-15 11:22:27 -05:00
e77dfde1bc reorganize to use .bat files instead of .sh. Move demo to an example project 2025-09-15 10:58:01 -05:00
530dea9728 fix path to LivePlotter.cs 2025-09-15 09:25:51 -05:00
8b38b8feff fix spelling mistake 2025-09-15 09:25:04 -05:00
d686ad7799 remove hacky testing script 2025-09-15 09:23:09 -05:00
11d8e9a865 move csharp example to examples folder 2025-09-15 09:22:23 -05:00
69d5ef09bf ignore future compile command files 2025-09-15 09:21:09 -05:00
2b9316f9d2 remove compile_commands.json. update glfw 2025-09-15 09:19:59 -05:00
2c20254c23 readme mods 2025-09-12 09:43:28 -05:00
90c3023618 readme mods 2025-09-11 14:47:54 -05:00
60ad2c5da5 readme mods 2025-09-11 14:37:56 -05:00
bedb7d93e9 remove unneeded files 2025-09-11 14:31:16 -05:00
7f0d52c3a0 Add readme and example csharp dll interface class. 2025-09-11 14:30:05 -05:00
29 changed files with 306 additions and 121 deletions

View File

@@ -1,8 +0,0 @@
# Format Style Options - Created with Clang Power Tools
---
BasedOnStyle: WebKit
BreakBeforeBraces: Attach
ColumnLimit: 110
SortIncludes: false
SpaceAfterTemplateKeyword: false
...

4
.gitignore vendored
View File

@@ -12,4 +12,6 @@ x64**
**.exe
.cache**
tags
bin/*
**/bin/**
**/obj/**
compile_commands.json

34
README.md Normal file
View File

@@ -0,0 +1,34 @@
# Overview
LivePlotter is an opengl-based, 3D point plotting DLL. It is designed to be integrated into any project that needs to see 3D points plotted and updated in real time. It supports simple camera controls: rotate, pan, and zoom. Points can be given a "lifetime" during which their opacity will fade. The opacity of these points can be restored by updating their position. The goal here is to create an intuitive representation of sensor readings (where the opacity of the point represents the age of the reading).
This DLL is designed to integrate easily into a C# application. A class that accomplishes this included. See [LivePlotter.cs](./examples/csharp/LivePlotter.cs). Unfortunately the DLL also expects the presence of several other files currently, so you'll need to ensure **EVERYTHING** from the bin/ folder (that contains `LivePlotter.dll`) is in your project's build output directory.
![til](./demo.gif)
*Please note that this project is still considered a work in progress. The API may change in future revisions*
# Acquiring the DLL
There are two main ways to get the DLL...
## Download from releases (recommended)
Download the DLL from the latest release on the [releases page](https://git.the-embedded-lab.com/shamilton/LivePlotter/releases).
## Build from source (not recommended)
I don't enjoy using Visual Studio and like to manage my own build process. I prefer something simple like a script to build and that's currently what I have.
1. You'll need an installation of MSVC (likely via downloading/installing Visual Studio and then installing the C++ build tools).
2. Get the source code. `git clone --recurse-submodules https://git.the-embedded-lab.com/shamilton/LivePlotter.git`
3. Build the glfw library. `cd ext/glfw`. `mkdir build && cd build && cmake ..`. Open GLFW.sln, Change the configuration to "Release, and build the solution.
4. Find `vcvars64.bat` in your installation and modify the path in [activate.bat](./activate.bat) to point to it.
5. Open cmd.exe to the root directory of the project
6. Run `activate`. Then `build release`.
7. A folder bin/ should exist with `LivePlotter.dll` inside.
8. Copy the entire contents of the bin folder to your project's build output directory
If issues arise, please feel free to create an issue on this repository.
See the csharp and cpp [example projects](./examples) for details on how to integrate the DLL with your codebase. You must have the project built to run the csharp project (it copies the contents of bin into its output directory).

View File

@@ -1 +1 @@
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" && bash
"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat"

Binary file not shown.

BIN
bin/LivePlotter.lib (Stored with Git LFS)

Binary file not shown.

12
build.bat Normal file
View File

@@ -0,0 +1,12 @@
@ECHO off
SET flags=-Od -ZI -MDd
SET config=Debug
IF "%1" == "release" (
SET flags=-O2 -MD
SET config=Release
)
SET srcs=src\*
mkdir bin obj
cl %srcs% ext\glfw\build\src\%config%\glfw3.lib kernel32.lib user32.lib Gdi32.lib Shell32.lib -I inc -I ext\glm -I ext\glfw\include %flags% -std:c++20 -MP -LD -Fo:obj\\ -Fe:bin\\LivePlotter.dll
COPY /Y assets\* bin
COPY /Y assets\shaders\* bin

View File

@@ -1,22 +0,0 @@
(
cd bin
rm *
demo_srcs=../src/demo/*.cpp
demo_srcs+=" ../src/util.cpp"
plotter_srcs=../src/*.cpp
glad_src=../src/glad.c
glfw_lib=../ext/glfw/build/src/Debug/glfw3.lib
if [ $# -eq 1 ] && [ "$1" == "release" ]
then
dll_flags="-O2 -MTd"
exe_flags="-O2 -MT"
else
dll_flags="-Od -ZI -MDd"
exe_flags="-Od -ZI -MD"
fi
echo $flags
cl $plotter_srcs $glad_src $glfw_lib kernel32.lib user32.lib Gdi32.lib Shell32.lib -I ../inc -I ../ext/glm -I ../ext/glfw/include $dll_flags -MP -std:c++20 -LD -FeLivePlotter.dll
cp ../src/shaders/* .
cp ../assets/* .
cl $demo_srcs -I ../inc -I ../ext/glm $exe_flags -MP -std:c++20 -Fedemo.exe
)

View File

@@ -1,8 +0,0 @@
[
{ "directory": "C:/Users/seth/Documents/repos/LivePlotter", "command": "cl src/body.cpp -I inc -I ext/glm -I ext/glfw/include -Od -std:c++20 -Fo", "file": "src/body.cpp" },
{ "directory": "C:/Users/seth/Documents/repos/LivePlotter", "command": "cl src/camera.cpp -I inc -I ext/glm -I ext/glfw/include -Od -std:c++20 -Fo", "file": "src/camera.cpp" },
{ "directory": "C:/Users/seth/Documents/repos/LivePlotter", "command": "cl src/demo/demo.cpp -I inc -I ext/glm -I ext/glfw/include -Od -std:c++20 -Fo", "file": "src/demo/demo.cpp" },
{ "directory": "C:/Users/seth/Documents/repos/LivePlotter", "command": "cl src/live_plotter.cpp -I inc -I ext/glm -I ext/glfw/include -Od -std:c++20 -Fo", "file": "src/live_plotter.cpp" },
{ "directory": "C:/Users/seth/Documents/repos/LivePlotter", "command": "cl src/shaders.cpp -I inc -I ext/glm -I ext/glfw/include -Od -std:c++20 -Fo", "file": "src/shaders.cpp" },
{ "directory": "C:/Users/seth/Documents/repos/LivePlotter", "command": "cl src/util.cpp -I inc -I ext/glm -I ext/glfw/include -Od -std:c++20 -Fo", "file": "src/util.cpp" },
]

View File

@@ -1,28 +0,0 @@
{
"rdocCaptureSettings": 1,
"settings": {
"autoStart": false,
"commandLine": "",
"environment": [
],
"executable": "C:\\Users\\sethh\\Documents\\repos\\LivePlotter\\bin\\demo.exe",
"inject": false,
"numQueuedFrames": 0,
"options": {
"allowFullscreen": true,
"allowVSync": true,
"apiValidation": false,
"captureAllCmdLists": false,
"captureCallstacks": false,
"captureCallstacksOnlyDraws": false,
"debugOutputMute": true,
"delayForDebugger": 0,
"hookIntoChildren": false,
"refAllResources": false,
"softMemoryLimit": 0,
"verifyBufferAccess": false
},
"queuedFrameCap": 0,
"workingDir": ""
}
}

View File

@@ -1,15 +0,0 @@
// raddbg 0.9.21 project file
recent_file: path: "src/live_plotter.cpp"
recent_file: path: "src/camera.cpp"
recent_file: path: "src/body.cpp"
recent_file: path: "src/shaders.cpp"
recent_file: path: "../../../../../Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.44.35207/include/variant"
recent_file: path: "../../../../../Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.44.35207/include/type_traits"
recent_file: path: "src/demo/demo.cpp"
target:
{
executable: "bin/demo.exe"
working_directory: bin
enabled: 1
}

View File

12
examples/cpp/build.bat Normal file
View File

@@ -0,0 +1,12 @@
SET flags=-Od -ZI -MD
IF "%1" == "release" (
SET flags=-O2 -MT
)
SET srcs=src\*
mkdir bin obj
(
PUSHD ..\..
CALL build.bat %1
POPD
COPY /Y ..\..\bin\* bin && cl %srcs% bin\LivePlotter.lib -I ..\..\inc -I ..\..\ext\glm %flags% -std:c++20 -MP -Fo:obj\\ -Fe:bin\\
)

22
examples/cpp/export.bat Normal file
View File

@@ -0,0 +1,22 @@
@ECHO off
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO [ > compile_commands.json
FOR /r "src\" %%F IN (*.cpp) DO (
SET "file=%%F"
SET "file=!file:\=/!"
SET "directory=%~dp0"
SET "directory=!directory:\=/!"
ECHO { >> compile_commands.json
ECHO "directory": "!directory!", >> compile_commands.json
ECHO "command": "cl !file! -I inc -I ../../ext/glm -I ../../ext/glfw/include %flags% -std:c++20 -MD -MP -Fo:obj\\ ", >> compile_commands.json
ECHO "file": "!file!" >> compile_commands.json
ECHO }, >> compile_commands.json
)
ECHO ] >> compile_commands.json

40
examples/cpp/src/util.cpp Normal file
View File

@@ -0,0 +1,40 @@
#include "util.hpp"
using namespace std;
bool read_file(string* s, const char* filepath) {
ifstream file(filepath);
if (!file.is_open()) {
return false;
}
*s = { istreambuf_iterator<char>(file), istreambuf_iterator<char>() };
return true;
}
vector<string> split_str(string s, char delim) {
vector<string> res;
string cur_word;
istringstream ss(s);
while (getline(ss, cur_word, delim)) {
res.push_back(cur_word);
}
return res;
}
// https://songho.ca/opengl/gl_quaternion.html
glm::mat4 quat_to_mat4(glm::quat q) {
glm::vec4 col0 = glm::vec4(
1 - 2 * q.y * q.y - 2 * q.z * q.z, 2 * q.x * q.y + 2 * q.w * q.z, 2 * q.x * q.z - 2 * q.w * q.y, 0);
glm::vec4 col1 = glm::vec4(
2 * q.x * q.y - 2 * q.w * q.z, 1 - 2 * q.x * q.x - 2 * q.z * q.z, 2 * q.y * q.z + 2 * q.w * q.x, 0);
glm::vec4 col2 = glm::vec4(
2 * q.x * q.z + 2 * q.w * q.y, 2 * q.y * q.z - 2 * q.w * q.x, 1 - 2 * q.x * q.x - 2 * q.y * q.y, 0);
glm::vec4 col3 = glm::vec4(0, 0, 0, 1);
return glm::mat4(col0, col1, col2, col3);
}
float randf() { return (float)abs(rand()) / (float)RAND_MAX; }

View File

@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="COPY /Y ..\..\bin\* $(OutDir)" />
</Target>
</Project>

25
examples/csharp/Demo.sln Normal file
View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36429.23 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo.csproj", "{EBDDE3D7-AF12-4A1F-B099-0D046C271859}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{EBDDE3D7-AF12-4A1F-B099-0D046C271859}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBDDE3D7-AF12-4A1F-B099-0D046C271859}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBDDE3D7-AF12-4A1F-B099-0D046C271859}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBDDE3D7-AF12-4A1F-B099-0D046C271859}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1E4249FD-1F82-4EE1-AF0B-68CAB69466C7}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,68 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using PointID = System.UInt64;
namespace Demo
{
public class LivePlotter
{
[DllImport("LivePlotter.dll")]
public static extern bool start(int win_w, int win_h);
[DllImport("LivePlotter.dll")]
public static extern bool stop();
[DllImport("LivePlotter.dll")]
public static extern PointID create_point(float x, float y, float z);
[DllImport("LivePlotter.dll")]
public static extern void set_color(PointID id, float r, float g, float b);
[DllImport("LivePlotter.dll")]
public static extern void set_scale(PointID id, float scale);
[DllImport("LivePlotter.dll")]
public static extern void update_point(PointID id, float x, float y, float z);
[DllImport("LivePlotter.dll")]
public static extern void set_lifetime(PointID id, float new_lifetime_s);
[DllImport("LivePlotter.dll")]
public static extern void clear_point(PointID id);
public static void PlotCameraPoses()
{
string[] lines = File.ReadAllLines("poses.csv");
for (int camera_i = 1; camera_i < lines.Length; camera_i++)
{
string[] words = lines[camera_i].Split(',');
float x = float.Parse(words[0]);
float y = float.Parse(words[1]);
float z = float.Parse(words[2]);
float w = float.Parse(words[3]);
float i = float.Parse(words[4]);
float j = float.Parse(words[5]);
float k = float.Parse(words[6]);
Pose pose = new(new Quaternion(i, j, k, w), new Vector3(x, y, z));
for (int m = 0; m < 3; m++)
{
for (int n = 0; n < 25; n++)
{
Vector3 local = new(0);
local[m] = n * 10.0f;
Vector3 global = pose * local;
PointID id = create_point(global.X, global.Y, global.Z);
Vector3 color = new(0);
color[m] = 1.0f;
set_color(id, color.X, color.Y, color.Z);
set_scale(id, 15);
}
}
}
}
}
}

40
examples/csharp/Pose.cs Normal file
View File

@@ -0,0 +1,40 @@
using System.Numerics;
namespace Demo
{
/// <summary>
/// <para>
/// Represents a rigid transformation from one coordinate system to another. <b>NOTE: By default, unless otherwise
/// stated, the <c>Pose</c> of an object transforms its local space to the ambient world space. </b>
/// </para>
/// <para>
/// Given two <c>Pose</c>s <c>p1</c> and <c>p2</c> which transform from space A to B and from B to C respectively,
/// the pose <c>p2 * p1</c> transforms from A to C, and <c>p1 * v</c> transforms a vector <c>v</c> from space A to
/// B. A <c>Pose</c> has a rotation and a translation. The rotation is always applied first, followed by the
/// translation.
/// </para>
/// </summary>
/// <param name="rotation">The rotation of this pose.</param>
/// <param name="translation">The translation of this pose.</param>
public struct Pose(Quaternion rotation, Vector3 translation)
{
public Vector3 Translation { readonly get; set; } = translation;
public Quaternion Rotation { readonly get; set; } = rotation;
public static Pose Identity => new(Quaternion.Identity, Vector3.Zero);
public Pose(Vector3 translation) : this(Quaternion.Identity, translation) { }
public Pose(Quaternion rotation) : this(rotation, Vector3.Zero) { }
public static Vector3 operator *(Pose p, Vector3 v) => Vector3.Transform(v, p.Rotation) + p.Translation;
public static Pose operator *(Pose a, Pose b) => new(a.Rotation * b.Rotation, a * b.Translation);
public readonly Pose Inverse
{
get
{
var rotInv = Quaternion.Conjugate(Rotation);
return new(rotInv, -Vector3.Transform(Translation, rotInv));
}
}
}
}

View File

@@ -0,0 +1,6 @@
using Demo;
LivePlotter.start(800, 800);
LivePlotter.PlotCameraPoses();
while (true) ;

22
export.bat Normal file
View File

@@ -0,0 +1,22 @@
@ECHO off
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO [ > compile_commands.json
FOR /r "src\" %%F IN (*.cpp) DO (
SET "file=%%F"
SET "file=!file:\=/!"
SET "directory=%~dp0"
SET "directory=!directory:\=/!"
ECHO { >> compile_commands.json
ECHO "directory": "!directory!", >> compile_commands.json
ECHO "command": "cl !file! -I inc -I ext/glm -I ext/glfw/include %flags% -std:c++20 -MP -LD -Fo:obj\\ ", >> compile_commands.json
ECHO "file": "!file!" >> compile_commands.json
ECHO }, >> compile_commands.json
)
ECHO ] >> compile_commands.json

View File

@@ -1,5 +0,0 @@
srcs=src/*
echo [ > compile_commands.json
find src -iname "*.cpp" -exec sh -c 'echo { \"directory\": \"$(cygpath -m $(pwd))\", \"command\": \"cl "$(cygpath -m {})" -I inc -I ext/glm -I ext/glfw/include -Od -std:c++20 -Fo\", \"file\": \"$(cygpath -m {})\" }, >> compile_commands.json' \;
echo ] >> compile_commands.json

View File

@@ -11,9 +11,12 @@
typedef unsigned int uint;
bool read_file(std::string* s, const char* filepath);
std::vector<std::string> split_str(std::string s, char delim);
using namespace std;
bool read_file(string* s, const char* filepath);
vector<string> split_str(string s, char delim);
glm::mat4 quat_to_mat4(glm::quat q);
float randf();
template<class T> T lerp(T start, T end, float t) { return t * end + (1 - t) * start; }
template<class T> float ilerp(T start, T end, T pos) { return (pos - start) / (end - start); }
template<class T> float ilerp(T start, T end, T pos) { return (pos - start) / (end - start); }

View File

@@ -1,7 +1,3 @@
#include <string>
#include <fstream>
#include <sstream>
#include "util.hpp"
using namespace std;

View File

@@ -1,22 +0,0 @@
import numpy as np
import subprocess
from pathlib import Path
import time
p = subprocess.Popen(Path("bin", "demo.exe"), cwd="bin", stdin=subprocess.PIPE)
lines = None
with open("assets/gpoints_rotate.obj", "r") as f:
lines = f.readlines()
period = 0.01
for i in range(len(lines)):
time.sleep(period)
words = lines[i].split()
x = words[1]
y = words[2]
z = words[3]
name = "".join(words[4:])
p.stdin.write(f"{name} {x} {y} {z}\n".encode("utf8"))
p.stdin.flush()