15 Commits
v0.1.0 ... main

26 changed files with 227 additions and 90 deletions

4
.gitignore vendored
View File

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

View File

@@ -1,31 +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 supposts simple camera control: 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. This allows an intuite representation of sensor readings (where the opacity of the point represents the age of the reading).
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 acomplishes this included. See [LivePlotter.cs](./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.
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. I will avoid making api-breaking changes. Functionality may be added and code refactoring may occur in the future since I plan to use this for several projects*
*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 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)
Building is a tad messy atm. I despise 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, but it's not nearly as clean as I'd like. I hope to fix this mess in the future.
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 git bash and an installation of MSVC (likely via downloading/installing Visual Studio and then installing the C++ build tools).
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`. Open GLFW.sln and build the solution.
4. Find `vcvars64.bat` in your installation and modify the path in [run_before_build.bat](./run_before_build.bat) to point to it.
5. Open git bash to the root directory of the project.
6. Run `./run_before_build.bat` then `build.sh`
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

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

@@ -1,10 +1,10 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using Common.Data;
using pointid = System.UInt64;
namespace TysonPalletScanner
using PointID = System.UInt64;
namespace Demo
{
public class LivePlotter
{
@@ -15,22 +15,22 @@ namespace TysonPalletScanner
public static extern bool stop();
[DllImport("LivePlotter.dll")]
public static extern pointid create_point(float x, float y, float z);
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);
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);
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);
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);
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 extern void clear_point(PointID id);
public static void PlotCameraPoses()
{
@@ -54,7 +54,7 @@ namespace TysonPalletScanner
Vector3 local = new(0);
local[m] = n * 10.0f;
Vector3 global = pose * local;
pointid id = create_point(global.X, global.Y, global.Z);
PointID id = create_point(global.X, global.Y, global.Z);
Vector3 color = new(0);
color[m] = 1.0f;

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()